import { Platform } from "react-native"; /** * Gishen design tokens. * * Brand lime is used as a *fill* with ink text (high contrast, distinctive). * `brandText` is the accessible green reserved for green-on-white text. */ const palette = { lime50: "#F6FAEC", lime100: "#ECF4D8", lime200: "#D9E9B2", lime300: "#C3DB86", lime400: "#A8C73A", lime500: "#93B22E", lime600: "#748E22", lime700: "#55681B", ink900: "#101410", ink700: "#2C332B", ink500: "#5B6157", ink300: "#8A9188", ink100: "#C3C8BE", paper: "#F7F8F5", white: "#FFFFFF", hairline: "#E9ECE2", wash: "#F1F3EC", red500: "#D64545", red50: "#FDF0F0", amber500: "#E0870F", amber50: "#FDF5E7", blue500: "#2F6FED", blue50: "#EEF3FE", } as const; export const colors = { brand: palette.lime400, brandDeep: palette.lime500, brandText: palette.lime600, brandSoft: palette.lime100, brandWash: palette.lime50, brandBorder: palette.lime200, ink: palette.ink900, inkDeep: palette.ink900, inkSecondary: palette.ink500, muted: palette.ink500, subtle: palette.ink300, disabled: palette.ink100, page: palette.paper, surface: palette.white, surfaceAlt: palette.wash, line: palette.hairline, danger: palette.red500, dangerSoft: palette.red50, warn: palette.amber500, warnSoft: palette.amber50, accent: palette.amber500, info: palette.blue500, infoSoft: palette.blue50, inStock: palette.lime600, outOfStock: palette.red500, star: "#F5A524", } as const; export const space = { xs: 4, sm: 8, md: 12, lg: 16, xl: 20, "2xl": 24, "3xl": 32, "4xl": 40, } as const; export const radii = { xs: 8, sm: 12, md: 16, lg: 20, xl: 24, card: 20, pill: 999, } as const; /** Screen gutter used by every page so content lines up across the app. */ export const gutter = 20; export const fonts = { regular: "Jakarta_400Regular", medium: "Jakarta_500Medium", semibold: "Jakarta_600SemiBold", bold: "Jakarta_700Bold", extrabold: "Jakarta_800ExtraBold", } as const; export type TypeVariant = | "display" | "h1" | "h2" | "h3" | "body" | "bodyMedium" | "bodySm" | "label" | "caption" | "micro" | "price" | "priceLg"; export const typography: Record< TypeVariant, { fontFamily: string; fontSize: number; lineHeight: number; letterSpacing?: number } > = { display: { fontFamily: fonts.extrabold, fontSize: 30, lineHeight: 36, letterSpacing: -0.6 }, h1: { fontFamily: fonts.extrabold, fontSize: 24, lineHeight: 30, letterSpacing: -0.4 }, h2: { fontFamily: fonts.bold, fontSize: 18, lineHeight: 24, letterSpacing: -0.2 }, h3: { fontFamily: fonts.semibold, fontSize: 15, lineHeight: 20, letterSpacing: -0.1 }, body: { fontFamily: fonts.regular, fontSize: 14, lineHeight: 21 }, bodyMedium: { fontFamily: fonts.medium, fontSize: 14, lineHeight: 20 }, bodySm: { fontFamily: fonts.regular, fontSize: 13, lineHeight: 19 }, label: { fontFamily: fonts.semibold, fontSize: 13, lineHeight: 17 }, caption: { fontFamily: fonts.medium, fontSize: 12, lineHeight: 16 }, micro: { fontFamily: fonts.semibold, fontSize: 11, lineHeight: 14, letterSpacing: 0.2 }, price: { fontFamily: fonts.bold, fontSize: 16, lineHeight: 21, letterSpacing: -0.2 }, priceLg: { fontFamily: fonts.extrabold, fontSize: 24, lineHeight: 29, letterSpacing: -0.5 }, }; type Shadow = { shadowColor: string; shadowOpacity: number; shadowRadius: number; shadowOffset: { width: number; height: number }; elevation: number; }; const shadow = (opacity: number, radius: number, y: number, elevation: number): Shadow => ({ shadowColor: "#16240A", shadowOpacity: Platform.OS === "android" ? opacity * 1.4 : opacity, shadowRadius: radius, shadowOffset: { width: 0, height: y }, elevation, }); export const shadows = { /** Resting cards. Nearly invisible, just lifts off the page. */ xs: shadow(0.04, 6, 2, 1), sm: shadow(0.05, 10, 4, 2), /** Sheets, floating tab bar, sticky bars. */ md: shadow(0.08, 18, 8, 8), lg: shadow(0.12, 28, 14, 16), } as const; /** Card recipe reused across screens so every surface matches. */ export const card = { backgroundColor: colors.surface, borderRadius: radii.card, borderWidth: 1, borderColor: colors.line, } as const; export const hitSlop = { top: 10, bottom: 10, left: 10, right: 10 } as const;