diff --git a/src/theme/colors.ts b/src/theme/colors.ts new file mode 100644 index 0000000..154a2a4 --- /dev/null +++ b/src/theme/colors.ts @@ -0,0 +1,46 @@ +export const colors = { + // Base palette — dark-first, tinted with deep blue + background: "#06060C", + surface: "#0E0E18", + surfaceElevated: "#16162A", + + // Glass materials + glass: { + background: "rgba(255, 255, 255, 0.06)", + backgroundHover: "rgba(255, 255, 255, 0.10)", + backgroundActive: "rgba(255, 255, 255, 0.14)", + border: "rgba(255, 255, 255, 0.10)", + borderLight: "rgba(255, 255, 255, 0.18)", + highlight: "rgba(255, 255, 255, 0.04)", + }, + + // Text + text: { + primary: "#F2F0ED", + secondary: "rgba(242, 240, 237, 0.60)", + tertiary: "rgba(242, 240, 237, 0.35)", + inverse: "#06060C", + }, + + // Accents — derived from user images at runtime, these are defaults + accent: { + primary: "rgba(180, 160, 255, 0.80)", + secondary: "rgba(120, 200, 255, 0.60)", + warm: "rgba(255, 180, 140, 0.70)", + }, + + // Semantic + success: "rgba(120, 220, 160, 0.80)", + error: "rgba(255, 120, 120, 0.80)", + warning: "rgba(255, 200, 100, 0.80)", + + // Shadows + shadow: { + soft: "rgba(0, 0, 0, 0.25)", + medium: "rgba(0, 0, 0, 0.40)", + strong: "rgba(0, 0, 0, 0.60)", + }, + + // Gradient mesh colors for empty states + meshGradient: ["#1a0533", "#0a1628", "#061a1a", "#0f0a1e"], +} as const; diff --git a/src/theme/glass.ts b/src/theme/glass.ts new file mode 100644 index 0000000..09d46d1 --- /dev/null +++ b/src/theme/glass.ts @@ -0,0 +1,55 @@ +import { Platform, ViewStyle } from "react-native"; +import { colors } from "./colors"; + +export const glassIntensity = { + subtle: 15, + medium: 25, + strong: 40, + heavy: 60, +} as const; + +export type GlassVariant = "subtle" | "medium" | "strong" | "heavy"; + +export const glassSurface = ( + variant: GlassVariant = "medium" +): ViewStyle => ({ + backgroundColor: colors.glass.background, + borderWidth: 1, + borderColor: colors.glass.border, + overflow: "hidden", + ...Platform.select({ + ios: { + shadowColor: colors.shadow.soft, + shadowOffset: { width: 0, height: 8 }, + shadowOpacity: variant === "strong" ? 0.3 : 0.15, + shadowRadius: 24, + }, + android: { + elevation: variant === "strong" ? 12 : 6, + }, + }), +}); + +export const glassRadius = { + xs: 8, + sm: 12, + md: 16, + lg: 24, + xl: 32, + pill: 999, +} as const; + +export const glassBorder = { + thin: { + borderWidth: 0.5, + borderColor: colors.glass.border, + }, + normal: { + borderWidth: 1, + borderColor: colors.glass.border, + }, + accent: { + borderWidth: 1, + borderColor: colors.glass.borderLight, + }, +} as const; diff --git a/src/theme/index.ts b/src/theme/index.ts new file mode 100644 index 0000000..da330ec --- /dev/null +++ b/src/theme/index.ts @@ -0,0 +1,5 @@ +export { colors } from "./colors"; +export { glassSurface, glassIntensity, glassRadius, glassBorder } from "./glass"; +export type { GlassVariant } from "./glass"; +export { typography } from "./typography"; +export { spacing, layout } from "./spacing"; diff --git a/src/theme/spacing.ts b/src/theme/spacing.ts new file mode 100644 index 0000000..35c325b --- /dev/null +++ b/src/theme/spacing.ts @@ -0,0 +1,17 @@ +export const spacing = { + xs: 4, + sm: 8, + md: 16, + lg: 24, + xl: 32, + xxl: 48, +} as const; + +export const layout = { + screenPadding: 20, + cardPadding: 16, + bottomSheetPeek: 180, + bottomSheetHalf: 360, + tabBarHeight: 72, + headerHeight: 56, +} as const; diff --git a/src/theme/typography.ts b/src/theme/typography.ts new file mode 100644 index 0000000..85e9e15 --- /dev/null +++ b/src/theme/typography.ts @@ -0,0 +1,76 @@ +import { TextStyle, Platform } from "react-native"; +import { colors } from "./colors"; + +const fontFamily = Platform.select({ + ios: "System", + android: "sans-serif", + default: "System", +}); + +const baseShadow: TextStyle = { + textShadowColor: "rgba(0, 0, 0, 0.5)", + textShadowOffset: { width: 0, height: 1 }, + textShadowRadius: 4, +}; + +export const typography = { + title: { + fontSize: 28, + fontWeight: "700", + color: colors.text.primary, + fontFamily, + letterSpacing: -0.5, + ...baseShadow, + } as TextStyle, + + heading: { + fontSize: 20, + fontWeight: "600", + color: colors.text.primary, + fontFamily, + letterSpacing: -0.3, + ...baseShadow, + } as TextStyle, + + body: { + fontSize: 16, + fontWeight: "400", + color: colors.text.primary, + fontFamily, + lineHeight: 22, + } as TextStyle, + + bodySecondary: { + fontSize: 14, + fontWeight: "400", + color: colors.text.secondary, + fontFamily, + lineHeight: 20, + } as TextStyle, + + caption: { + fontSize: 12, + fontWeight: "500", + color: colors.text.tertiary, + fontFamily, + letterSpacing: 0.2, + } as TextStyle, + + label: { + fontSize: 13, + fontWeight: "600", + color: colors.text.secondary, + fontFamily, + letterSpacing: 0.5, + textTransform: "uppercase", + } as TextStyle, + + button: { + fontSize: 16, + fontWeight: "600", + color: colors.text.primary, + fontFamily, + letterSpacing: 0.3, + ...baseShadow, + } as TextStyle, +} as const;