Ship Expo Router screens, seeded stores, and polished UX so auth, shop, checkout, prescriptions, loyalty, family, and account processes work end-to-end offline. Co-authored-by: Cursor <cursoragent@cursor.com>
169 lines
4.2 KiB
TypeScript
169 lines
4.2 KiB
TypeScript
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;
|