/** * Design tokens for the Superagent native package. * * Single source of truth, mirroring the web design system * (frontend/packages/design-system/src/tokens/tokens-vars.css). React Native * can't consume the DS CSS variables / Tailwind preset directly, so the * foundation palette, spacing, radius, typography and shadow scales are * transcribed here verbatim, and the DS semantic tokens are expressed as * per-scheme objects (light / dark). * * Style files keep authoring against the dark palette; theme.ts maps each * authored hex onto the matching DS semantic role and resolves it to these * values for the active color scheme. */ import { Platform } from 'react-native'; // ── Foundation palette (raw hex, constant across modes) ────────────────── export const palette = { white: '#ffffff', stone50: '#f8f6f4', stone100: '#f1eeea', stone200: '#e9e6e0', stone300: '#ddd8d2', stone400: '#c9c3bc', stone500: '#ada69f', stone600: '#8e8780', stone700: '#6f6963', stone800: '#514c47', stone900: '#312d2a', stone950: '#1c1a18', neutral50: '#fcfcfc', neutral100: '#f5f5f4', neutral200: '#ebebeb', neutral300: '#c0c0c0', neutral400: '#929292', neutral500: '#5d5d5d', neutral600: '#505050', neutral700: '#3a3939', neutral800: '#252424', neutral900: '#1b1c1e', neutral950: '#0f0e0e', orange50: '#FFF3EA', orange100: '#FFE4D1', orange200: '#FFC9A6', orange300: '#FFA875', orange400: '#FF8E5D', orange500: '#FF773C', orange600: '#E8641B', orange700: '#BF4513', orange800: '#99370f', orange900: '#73280a', orange950: '#4d1a06', brandLogo: '#FF6A00', purple50: '#efefff', purple100: '#e2e2ff', purple200: '#d0d0f5', purple300: '#a79fff', purple400: '#7e73f2', purple500: '#4d33de', purple600: '#4029bf', purple700: '#34219c', purple800: '#271878', purple900: '#312e81', purple950: '#232337', red50: '#fdf2f2', red100: '#fce2e3', red200: '#f8c7c8', red300: '#f29fa1', red400: '#ea6e71', red500: '#df3336', red600: '#bf2124', red700: '#9c2426', red800: '#681719', red900: '#4c1112', red950: '#300a0b', green50: '#f2f8f3', green100: '#e4f0e6', green200: '#cbe0cf', green300: '#a7c8ae', green400: '#7ead88', green500: '#568f63', green600: '#347043', green700: '#134f25', green800: '#0f401e', green900: '#0b3016', green950: '#061f0d', yellow50: '#fff9ee', yellow100: '#fff4db', yellow200: '#ffeab5', yellow300: '#f9df8c', yellow400: '#ffd15f', yellow500: '#f0c84d', yellow600: '#ddb440', yellow700: '#be9734', yellow800: '#9b7928', yellow900: '#735a1d', yellow950: '#473611', blue50: '#f1f4ff', blue100: '#e3e9ff', blue200: '#ccd6ff', blue300: '#aab9ff', blue400: '#7e90f5', blue500: '#3b51e5', blue600: '#3145c5', blue700: '#2838a5', blue800: '#0a1e8f', blue900: '#07176b', blue950: '#040f47', } as const; // ── Spacing (4px grid) ─────────────────────────────────────────────────── export const space = { none: 0, xs: 4, // --space-1 sm: 6, // --space-1-5 md: 8, // --space-2 lg: 12, // --space-3 xl: 16, // --space-4 '2xl': 20, // --space-5 '3xl': 24, // --space-6 '4xl': 32, // --space-8 '5xl': 40, // --space-10 '6xl': 48, // --space-12 '7xl': 64, // --space-16 } as const; // ── Border radii ───────────────────────────────────────────────────────── export const radius = { none: 0, xs: 2, sm: 4, md: 6, lg: 8, xl: 12, '2xl': 16, '3xl': 24, '4xl': 32, card: 10, pill: 100, full: 9999, } as const; // ── Typography ─────────────────────────────────────────────────────────── // Native consumers must bundle the "Wix Madefor" families. The ttfs shipped by // the mobile engine register under the " App"-suffixed family names // (WixMadeforTextApp / WixMadeforDisplayApp → "Wix Madefor Text App" / // "Wix Madefor Display App"), which is what iOS/Android match on. The web demo // loads the un-suffixed families from Google Fonts, so the name differs per // platform — resolve the suffix at runtime rather than picking one and having // the other silently fall back to the system font. const madeforSuffix = Platform.OS === 'web' ? '' : ' App'; export const fontFamily = { body: `Wix Madefor Text${madeforSuffix}`, display: `Wix Madefor Display${madeforSuffix}`, mono: 'Menlo', } as const; export const fontSize = { tiny: 12, small: 14, medium: 16, h6: 12, h5: 14, h4: 16, h3: 20, h2: 24, h1: 32, } as const; export const fontWeight = { regular: '400', medium: '500', semibold: '600', bold: '700', } as const; // Line-height scale (--lh-N from the DS). export const lineHeight = { '4': 16, '5': 18, '6': 20, '7': 24, '9': 32, '11': 40, } as const; // ── Composed text styles (DS .h*, .text-*, .body-* utility classes) ────── // Headings use the DS title weights; body copy uses the body family. The // native package bundles only "Wix Madefor Text", so every style uses that // family (the DS title face falls back to it here). type TextStyle = { fontFamily: string; fontWeight: string; fontSize: number; lineHeight: number; }; const t = (fontWeight: string, fontSize: number, lh: number): TextStyle => ({ fontFamily: fontFamily.body, fontWeight, fontSize, lineHeight: lh, }); export const textStyles = { h1: t(fontWeight.medium, fontSize.h1, lineHeight['11']), // 32 / 40 h2: t(fontWeight.medium, fontSize.h2, lineHeight['9']), // 24 / 32 h3: t(fontWeight.semibold, fontSize.h3, lineHeight['7']), // 20 / 24 h4: t(fontWeight.semibold, fontSize.h4, lineHeight['7']), // 16 / 24 h5: t(fontWeight.semibold, fontSize.h5, lineHeight['6']), // 14 / 20 h6: t(fontWeight.semibold, fontSize.h6, lineHeight['4']), // 12 / 16 body: t(fontWeight.regular, fontSize.medium, lineHeight['7']), bodyMedium: t(fontWeight.medium, fontSize.medium, lineHeight['7']), bodySemibold: t(fontWeight.semibold, fontSize.medium, lineHeight['7']), bodyBold: t(fontWeight.bold, fontSize.medium, lineHeight['7']), small: t(fontWeight.regular, fontSize.small, lineHeight['5']), smallMedium: t(fontWeight.medium, fontSize.small, lineHeight['5']), smallSemibold: t(fontWeight.semibold, fontSize.small, lineHeight['5']), smallBold: t(fontWeight.bold, fontSize.small, lineHeight['5']), tiny: t(fontWeight.regular, fontSize.tiny, lineHeight['4']), tinyMedium: t(fontWeight.medium, fontSize.tiny, lineHeight['4']), tinySemibold: t(fontWeight.semibold, fontSize.tiny, lineHeight['4']), tinyBold: t(fontWeight.bold, fontSize.tiny, lineHeight['4']), } as const; /** * DS type ramp keyed by font size. Authored sizes are snapped to the nearest * rung so off-scale values (10/11/13/15/17/18/21/22/23…) resolve to a DS size * and its paired line height. `[upperBound, fontSize, lineHeight]`: the first * row whose upper bound is >= the authored size wins. */ export const TYPE_RAMP: ReadonlyArray = [ [12, 12, lineHeight['4']], // tiny → 12 / 16 (≤12) [14, 14, lineHeight['5']], // small → 14 / 18 (13–14) [18, 16, lineHeight['7']], // medium → 16 / 24 (15–18) [21, 20, lineHeight['7']], // h3 → 20 / 24 (19–21) [27, 24, lineHeight['9']], // h2 → 24 / 32 (22–27) [Infinity, 32, lineHeight['11']], // h1 → 32 / 40 ] as const; // ── Shadows (RN shape) ─────────────────────────────────────────────────── export const shadow = { md: { shadowColor: '#000000', shadowOffset: { height: 8, width: 0 }, shadowOpacity: 0.35, shadowRadius: 14, elevation: 8, }, lg: { shadowColor: '#000000', shadowOffset: { height: 20, width: 0 }, shadowOpacity: 0.35, shadowRadius: 30, elevation: 12, }, } as const; // ── Semantic tokens, per scheme ────────────────────────────────────────── const p = palette; export interface SemanticTokens { surface: { canvas: string; sunken: string; surface: string; muted: string; elevated: string; white: string; brand: string; divider: string; dotMuted: string; actionPrimary: string; successSoft: string; warningSoft: string; dangerSoft: string; infoSoft: string; accentSoft: string; accentStrong: string; premiumSoft: string; // DS chip/filter fills (--sem-chips-fill-*): borderless filled chips whose // selected state is a stronger stone fill, not a dark pill. chipFill: string; chipFillActive: string; // --sem-surface-interactive-stone-default: the DS ClickableCard skin="stone" // fill used by selectable option rows. interactiveStone: string; }; fg: { default: string; secondary: string; muted: string; subtle: string; onAction: string; onWhite: string; accent: string; danger: string; success: string; warning: string; info: string; link: string; }; border: { default: string; subtle: string; strong: string; inverse: string; danger: string; warning: string; success: string; accent: string; }; } export const semanticDark: SemanticTokens = { surface: { canvas: p.neutral950, sunken: '#0a0909', // --surface-sunken (dark) surface: p.neutral900, muted: p.neutral800, elevated: p.neutral800, white: p.white, brand: p.orange500, divider: '#2a2828', dotMuted: p.neutral400, actionPrimary: p.neutral50, successSoft: 'rgba(70,149,108,0.16)', warningSoft: 'rgba(249,195,77,0.12)', dangerSoft: 'rgba(223,51,54,0.14)', infoSoft: 'rgba(59,81,229,0.16)', accentSoft: 'rgba(77,51,222,0.20)', accentStrong: p.purple800, premiumSoft: 'rgba(255,119,60,0.14)', chipFill: p.neutral600, chipFillActive: p.neutral400, interactiveStone: p.stone900, }, fg: { default: p.neutral50, secondary: p.neutral200, muted: p.neutral400, subtle: p.neutral500, onAction: p.neutral950, onWhite: p.neutral950, accent: p.orange400, danger: p.red400, success: p.green400, warning: p.yellow400, info: p.blue300, link: p.blue300, }, border: { default: '#2a2828', // --border-default (dark) subtle: '#1f1e1e', // --border-subtle (dark) strong: p.neutral700, inverse: p.neutral50, danger: p.red800, warning: p.orange800, success: p.green700, accent: p.purple800, }, }; export const semanticLight: SemanticTokens = { surface: { canvas: p.stone50, sunken: p.white, surface: p.white, muted: p.stone100, elevated: p.white, white: p.white, brand: p.orange500, divider: p.stone200, dotMuted: p.neutral500, actionPrimary: p.neutral950, successSoft: p.green100, warningSoft: p.yellow100, dangerSoft: p.red100, infoSoft: p.blue100, accentSoft: p.purple100, accentStrong: p.purple100, premiumSoft: p.orange100, chipFill: p.stone200, chipFillActive: p.stone400, interactiveStone: p.stone50, }, fg: { default: p.neutral950, secondary: p.neutral700, muted: p.neutral500, subtle: p.stone600, onAction: p.neutral50, onWhite: p.neutral950, accent: p.orange600, danger: p.red600, success: p.green600, warning: p.yellow700, info: p.blue600, link: p.blue600, }, border: { default: p.stone200, subtle: p.stone200, strong: p.stone400, inverse: p.neutral950, danger: p.red300, warning: p.orange300, success: p.green300, accent: p.purple200, }, }; export const semanticTokens = { dark: semanticDark, light: semanticLight } as const;