@krapwoo/ds-viewer 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -78
- package/dist/cli/config.js +26 -1
- package/dist/cli/dev.js +50 -5
- package/dist/cli/glob.js +8 -0
- package/dist/cli/init.js +148 -18
- package/dist/cli/main.js +102 -3
- package/dist/cli/props.js +166 -18
- package/dist/cli/sync.js +17 -1
- package/dist/cli/workspace.js +49 -21
- package/native/catalog/CatalogShell.tsx +6 -1
- package/native/catalog/CatalogSidebar.tsx +98 -5
- package/native/catalog/TokenRow.tsx +3 -2
- package/native/catalog/index.ts +6 -5
- package/native/catalog/logoLayout.ts +12 -0
- package/native/catalog/pageApi.ts +10 -1
- package/native/catalog/tokens.ts +5 -0
- package/package.json +11 -7
- package/starter-kit/WHEN_TO_USE.md +124 -0
- package/starter-kit/components/AnimatedChevron/AnimatedChevron.catalog.tsx +23 -0
- package/starter-kit/components/AnimatedChevron/AnimatedChevron.tsx +46 -0
- package/starter-kit/components/AnimatedChevron/index.ts +2 -0
- package/starter-kit/components/Avatar/Avatar.catalog.tsx +72 -0
- package/starter-kit/components/Avatar/Avatar.tsx +74 -0
- package/starter-kit/components/Avatar/index.ts +2 -0
- package/starter-kit/components/Badge/Badge.catalog.tsx +96 -0
- package/starter-kit/components/Badge/Badge.tsx +56 -0
- package/starter-kit/components/Badge/Badge.types.ts +18 -0
- package/starter-kit/components/Badge/index.ts +2 -0
- package/starter-kit/components/Banner/Banner.catalog.tsx +87 -0
- package/starter-kit/components/Banner/Banner.tsx +257 -0
- package/starter-kit/components/Banner/Banner.types.ts +38 -0
- package/starter-kit/components/Banner/index.ts +2 -0
- package/starter-kit/components/BottomSheet/BottomSheet.catalog.tsx +77 -0
- package/starter-kit/components/BottomSheet/BottomSheet.tsx +151 -0
- package/starter-kit/components/BottomSheet/index.ts +2 -0
- package/starter-kit/components/Button/Button.catalog.tsx +113 -0
- package/starter-kit/components/Button/Button.tsx +173 -0
- package/starter-kit/components/Button/Button.types.ts +52 -0
- package/starter-kit/components/Button/index.ts +2 -0
- package/starter-kit/components/ButtonGroup/ButtonGroup.catalog.tsx +39 -0
- package/starter-kit/components/ButtonGroup/ButtonGroup.tsx +84 -0
- package/starter-kit/components/ButtonGroup/index.ts +2 -0
- package/starter-kit/components/Card/Card.catalog.tsx +68 -0
- package/starter-kit/components/Card/Card.tsx +133 -0
- package/starter-kit/components/Card/index.ts +2 -0
- package/starter-kit/components/Checkbox/Checkbox.catalog.tsx +28 -0
- package/starter-kit/components/Checkbox/Checkbox.tsx +104 -0
- package/starter-kit/components/Checkbox/index.ts +2 -0
- package/starter-kit/components/Dialog/Dialog.catalog.tsx +41 -0
- package/starter-kit/components/Dialog/Dialog.tsx +82 -0
- package/starter-kit/components/Dialog/index.ts +2 -0
- package/starter-kit/components/Divider/Divider.catalog.tsx +35 -0
- package/starter-kit/components/Divider/Divider.tsx +20 -0
- package/starter-kit/components/Divider/index.ts +2 -0
- package/starter-kit/components/Dock/Dock.catalog.tsx +71 -0
- package/starter-kit/components/Dock/Dock.tsx +58 -0
- package/starter-kit/components/Dock/index.ts +2 -0
- package/starter-kit/components/Dropdown/Dropdown.catalog.tsx +69 -0
- package/starter-kit/components/Dropdown/Dropdown.tsx +162 -0
- package/starter-kit/components/Dropdown/index.ts +2 -0
- package/starter-kit/components/EmptyState/EmptyState.catalog.tsx +67 -0
- package/starter-kit/components/EmptyState/EmptyState.tsx +88 -0
- package/starter-kit/components/EmptyState/index.ts +2 -0
- package/starter-kit/components/FieldContainer/FieldContainer.catalog.tsx +76 -0
- package/starter-kit/components/FieldContainer/FieldContainer.tsx +99 -0
- package/starter-kit/components/FieldContainer/FieldContainer.types.ts +27 -0
- package/starter-kit/components/FieldContainer/index.ts +2 -0
- package/starter-kit/components/InputClearButton/InputClearButton.catalog.tsx +18 -0
- package/starter-kit/components/InputClearButton/InputClearButton.tsx +63 -0
- package/starter-kit/components/InputClearButton/index.ts +2 -0
- package/starter-kit/components/InputField/InputField.catalog.tsx +76 -0
- package/starter-kit/components/InputField/InputField.tsx +418 -0
- package/starter-kit/components/InputField/index.ts +2 -0
- package/starter-kit/components/List/List.catalog.tsx +30 -0
- package/starter-kit/components/List/List.tsx +36 -0
- package/starter-kit/components/List/index.ts +2 -0
- package/starter-kit/components/ListItem/ListItem.catalog.tsx +89 -0
- package/starter-kit/components/ListItem/ListItem.tsx +165 -0
- package/starter-kit/components/ListItem/index.ts +2 -0
- package/starter-kit/components/Loading/Loading.catalog.tsx +34 -0
- package/starter-kit/components/Loading/Loading.tsx +121 -0
- package/starter-kit/components/Loading/index.ts +2 -0
- package/starter-kit/components/Pill/Pill.catalog.tsx +93 -0
- package/starter-kit/components/Pill/Pill.tsx +181 -0
- package/starter-kit/components/Pill/Pill.types.ts +30 -0
- package/starter-kit/components/Pill/index.ts +2 -0
- package/starter-kit/components/PillRow/PillRow.catalog.tsx +91 -0
- package/starter-kit/components/PillRow/PillRow.tsx +140 -0
- package/starter-kit/components/PillRow/PillRow.types.ts +24 -0
- package/starter-kit/components/PillRow/index.ts +2 -0
- package/starter-kit/components/ProgressDots/ProgressDots.catalog.tsx +22 -0
- package/starter-kit/components/ProgressDots/ProgressDots.tsx +42 -0
- package/starter-kit/components/ProgressDots/index.ts +2 -0
- package/starter-kit/components/Radio/Radio.catalog.tsx +38 -0
- package/starter-kit/components/Radio/Radio.tsx +114 -0
- package/starter-kit/components/Radio/index.ts +2 -0
- package/starter-kit/components/Radio/usePressScale.ts +33 -0
- package/starter-kit/components/SearchField/SearchField.catalog.tsx +28 -0
- package/starter-kit/components/SearchField/SearchField.tsx +98 -0
- package/starter-kit/components/SearchField/index.ts +2 -0
- package/starter-kit/components/SectionHeader/SectionHeader.catalog.tsx +72 -0
- package/starter-kit/components/SectionHeader/SectionHeader.tsx +139 -0
- package/starter-kit/components/SectionHeader/index.ts +2 -0
- package/starter-kit/components/SegmentedToggle/SegmentedToggle.catalog.tsx +31 -0
- package/starter-kit/components/SegmentedToggle/SegmentedToggle.tsx +163 -0
- package/starter-kit/components/SegmentedToggle/index.ts +2 -0
- package/starter-kit/components/SegmentedToggle/useSlideAnim.ts +29 -0
- package/starter-kit/components/Shimmer/Breathing.catalog.tsx +29 -0
- package/starter-kit/components/Shimmer/Shimmer.catalog.tsx +89 -0
- package/starter-kit/components/Shimmer/Shimmer.tsx +158 -0
- package/starter-kit/components/Shimmer/Shimmer.types.ts +27 -0
- package/starter-kit/components/Shimmer/SkeletonGroup.catalog.tsx +39 -0
- package/starter-kit/components/Shimmer/index.ts +2 -0
- package/starter-kit/components/Surface/Surface.catalog.tsx +44 -0
- package/starter-kit/components/Surface/Surface.tsx +45 -0
- package/starter-kit/components/Surface/index.ts +2 -0
- package/starter-kit/components/Switch/Switch.catalog.tsx +33 -0
- package/starter-kit/components/Switch/Switch.tsx +129 -0
- package/starter-kit/components/Switch/index.ts +2 -0
- package/starter-kit/components/TextArea/TextArea.catalog.tsx +29 -0
- package/starter-kit/components/TextArea/TextArea.tsx +99 -0
- package/starter-kit/components/TextArea/index.ts +2 -0
- package/starter-kit/components/Toast/Toast.catalog.tsx +61 -0
- package/starter-kit/components/Toast/Toast.tsx +144 -0
- package/starter-kit/components/Toast/index.ts +2 -0
- package/starter-kit/components/Tooltip/Tooltip.catalog.tsx +121 -0
- package/starter-kit/components/Tooltip/Tooltip.tsx +195 -0
- package/starter-kit/components/Tooltip/index.ts +2 -0
- package/starter-kit/components/TopNav/TopNav.catalog.tsx +71 -0
- package/starter-kit/components/TopNav/TopNav.tsx +77 -0
- package/starter-kit/components/TopNav/index.ts +2 -0
- package/starter-kit/components/UnderlineTabs/UnderlineTabs.catalog.tsx +30 -0
- package/starter-kit/components/UnderlineTabs/UnderlineTabs.tsx +156 -0
- package/starter-kit/components/UnderlineTabs/index.ts +2 -0
- package/starter-kit/components/index.ts +36 -0
- package/starter-kit/components/statusColors.ts +27 -0
- package/starter-kit/icons/Icon.native.tsx +90 -0
- package/starter-kit/icons/index.ts +10 -0
- package/starter-kit/icons/paths.ts +623 -0
- package/starter-kit/icons/types.ts +69 -0
- package/starter-kit/pages/Colors.catalog.tsx +55 -0
- package/starter-kit/pages/Font.catalog.tsx +45 -0
- package/starter-kit/pages/Icons.catalog.tsx +51 -0
- package/starter-kit/pages/Manifest.catalog.tsx +24 -0
- package/starter-kit/pages/Motion.catalog.tsx +101 -0
- package/starter-kit/pages/Radius.catalog.tsx +35 -0
- package/starter-kit/pages/ReportIssue.catalog.tsx +148 -0
- package/starter-kit/pages/SavedTrips.catalog.tsx +247 -0
- package/starter-kit/pages/Shadow.catalog.tsx +34 -0
- package/starter-kit/pages/Spacing.catalog.tsx +12 -0
- package/starter-kit/pages/TripPlannerForm.catalog.tsx +48 -0
- package/starter-kit/pages/Typography.catalog.tsx +17 -0
- package/starter-kit/tokens/index.ts +50 -0
- package/starter-kit/tokens/motion.ts +90 -0
- package/starter-kit/tokens/palette.ts +111 -0
- package/starter-kit/tokens/scales.ts +106 -0
- package/starter-kit/tokens/semantic.ts +98 -0
- package/starter-kit/tokens/shadow.ts +36 -0
- package/starter-kit/tokens/typography.ts +72 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Numeric scales — spacing, radius, icon size. Values are in px/points (identical on RN and web).
|
|
3
|
+
*
|
|
4
|
+
* Spacing keys mirror a Figma-style scale where the token number ÷ 50 = px (800 → 16). Reference
|
|
5
|
+
* tokens directly at call sites (`DS_SPACING[800]`, `DS_RADIUS.medium`) — the convention every
|
|
6
|
+
* component here follows; the key unions are typo-safe already, so no accessor wrapper is needed.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// ─── Spacing ────────────────────────────────────────────────────────────────
|
|
10
|
+
export const DS_SPACING_STEPS = [
|
|
11
|
+
'none',
|
|
12
|
+
100, 200, 300, 400, 600, 800, 1200, 1600, 2000, 2400, 2800, 3200,
|
|
13
|
+
] as const;
|
|
14
|
+
export type SpacingStep = (typeof DS_SPACING_STEPS)[number];
|
|
15
|
+
|
|
16
|
+
export const DS_SPACING = {
|
|
17
|
+
none: 0,
|
|
18
|
+
100: 2,
|
|
19
|
+
200: 4,
|
|
20
|
+
300: 6,
|
|
21
|
+
400: 8,
|
|
22
|
+
600: 12,
|
|
23
|
+
800: 16,
|
|
24
|
+
1200: 24,
|
|
25
|
+
1600: 32,
|
|
26
|
+
2000: 40,
|
|
27
|
+
2400: 48,
|
|
28
|
+
2800: 56,
|
|
29
|
+
3200: 64,
|
|
30
|
+
} as const;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* When to reach for each spacing step — grounded in how the existing components actually use them
|
|
34
|
+
* (surveyed across native/components + the source app), not aspirational. Meant to be read by
|
|
35
|
+
* whoever (human or AI) is deciding which step fits a new layout; also rendered in the catalog's
|
|
36
|
+
* Spacing page. When in doubt, reach for 800 first — it's what most components converge on.
|
|
37
|
+
*/
|
|
38
|
+
export const DS_SPACING_USE: Record<SpacingStep, string> = {
|
|
39
|
+
none: 'Flush — no gap, e.g. an icon glued directly to adjacent content.',
|
|
40
|
+
100: "Micro-gap inside a tightly clustered pair — a ghost button's icon+label, a segmented toggle's track inset/segment gap.",
|
|
41
|
+
200: "Tight gap between closely related elements — a section header's icon+title, a tab's icon+label, a pill's internal gap.",
|
|
42
|
+
300: "A small nudge, rarely needed — e.g. a toast's icon-to-text gap when 200 reads cramped and 400 too loose.",
|
|
43
|
+
400: "Compact standard gap/padding — a status dot to its label, a pill's tight horizontal padding.",
|
|
44
|
+
600: "Medium gap/padding — a nested card's internal gap, a pill row's spacing, a row's horizontal padding.",
|
|
45
|
+
800: "The default — card padding, a button's vertical padding, most rows' gap. Reach for this first.",
|
|
46
|
+
1200: "Large horizontal padding — e.g. a large button's side padding.",
|
|
47
|
+
1600: 'Section-level spacing between stacked blocks on a screen (margin/gap between sections, not inside a component).',
|
|
48
|
+
2000: "A fixed control dimension, not a gap — e.g. an icon-only pill's height.",
|
|
49
|
+
2400: "Bottom padding for a scrollable screen's content.",
|
|
50
|
+
2800: 'Reserved — no current consumer; a step between 2400 and 3200 if both read wrong.',
|
|
51
|
+
3200: 'Large bottom padding for sheets/scroll content that must clear a safe area or tab bar.',
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// ─── Radius ─────────────────────────────────────────────────────────────────
|
|
55
|
+
export const DS_RADIUS_STEPS = ['none', 'xs', 'small', 'medium', 'large', 'round'] as const;
|
|
56
|
+
export type RadiusStep = (typeof DS_RADIUS_STEPS)[number];
|
|
57
|
+
|
|
58
|
+
export const DS_RADIUS = {
|
|
59
|
+
none: 0,
|
|
60
|
+
xs: 4,
|
|
61
|
+
small: 8,
|
|
62
|
+
medium: 12,
|
|
63
|
+
large: 24,
|
|
64
|
+
round: 999,
|
|
65
|
+
} as const;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* When to reach for each radius step — grounded in how the existing components actually use them
|
|
69
|
+
* (surveyed across native/components), not aspirational.
|
|
70
|
+
*/
|
|
71
|
+
export const DS_RADIUS_USE: Record<RadiusStep, string> = {
|
|
72
|
+
none: 'Flush corners — no rounding.',
|
|
73
|
+
xs: "Small corner softening — a banner's small action button, tiny placeholder blocks.",
|
|
74
|
+
small: "Compact rounding — a ghost button, a text-shimmer placeholder, UnderlineTabs' sliding indicator.",
|
|
75
|
+
medium: "The default card/field radius — Banner's callout, FieldContainer's input chrome, Toast, a container shimmer.",
|
|
76
|
+
large: "Prominent rounding for large surfaces — Dialog, BottomSheet's top corners.",
|
|
77
|
+
round: "Fully circular/pill — Button, Pill, Badge, Status dot, SegmentedToggle's track/thumb. Reach for this on anything meant to read as a chip, dot, or pill.",
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// ─── Icon size ────────────────────────────────────────────────────────────────
|
|
81
|
+
/**
|
|
82
|
+
* Icon sizes pair with text scales — use the same step as the text beside the icon.
|
|
83
|
+
* xxs (12) tight badges · xs (14) small label rows · sm (16) standard inline (default)
|
|
84
|
+
* md (20) prominent inline / tabs · lg (24) nav / actions · xl (32) section leads · 2xl (40) hero
|
|
85
|
+
*/
|
|
86
|
+
export const DS_ICON_SIZE_STEPS = ['xxs', 'xs', 'sm', 'md', 'lg', 'xl', '2xl'] as const;
|
|
87
|
+
export type IconSizeStep = (typeof DS_ICON_SIZE_STEPS)[number];
|
|
88
|
+
|
|
89
|
+
export const DS_ICON_SIZE: Record<IconSizeStep, number> = {
|
|
90
|
+
xxs: 12,
|
|
91
|
+
xs: 14,
|
|
92
|
+
sm: 16,
|
|
93
|
+
md: 20,
|
|
94
|
+
lg: 24,
|
|
95
|
+
xl: 32,
|
|
96
|
+
'2xl': 40,
|
|
97
|
+
} as const;
|
|
98
|
+
|
|
99
|
+
// ─── Accessibility ──────────────────────────────────────────────────────────────
|
|
100
|
+
/**
|
|
101
|
+
* Minimum tappable width/height (iOS's own 44pt guideline) — every control smaller than this pads
|
|
102
|
+
* its touch target out to it via `hitSlop` (small controls: Checkbox, Radio, Switch) or `minHeight`
|
|
103
|
+
* (row-shaped controls: ListItem, SegmentedToggle, UnderlineTabs, TopNav's icon slots), rather than
|
|
104
|
+
* each one hardcoding the number `44` independently.
|
|
105
|
+
*/
|
|
106
|
+
export const DS_A11Y_MIN_TOUCH_TARGET = 44;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic color tokens — the layer app code should consume.
|
|
3
|
+
*
|
|
4
|
+
* Every value points at a palette step (or a deliberate one-off). Components reference these names
|
|
5
|
+
* (surface.main, text.regular, emphasis.positive…) not raw hexes, so a rebrand is: edit palette.ts,
|
|
6
|
+
* then re-point anything here you want to shift. Grouped by role: surface, text, element, interaction,
|
|
7
|
+
* border, emphasis (saturated accents), shade (tinted fills).
|
|
8
|
+
*/
|
|
9
|
+
import { DS_PALETTE } from './palette';
|
|
10
|
+
|
|
11
|
+
const grey = DS_PALETTE.grey;
|
|
12
|
+
const green = DS_PALETTE.green;
|
|
13
|
+
const blue = DS_PALETTE.blue;
|
|
14
|
+
const red = DS_PALETTE.red;
|
|
15
|
+
const yellow = DS_PALETTE.yellow;
|
|
16
|
+
const purple = DS_PALETTE.purple;
|
|
17
|
+
|
|
18
|
+
export const DS_SEMANTIC = {
|
|
19
|
+
surface: {
|
|
20
|
+
main: grey[100],
|
|
21
|
+
muted: grey[300],
|
|
22
|
+
white: grey[0],
|
|
23
|
+
onTap: grey[100],
|
|
24
|
+
inverse: grey[800],
|
|
25
|
+
/** Translucent secondary fill — secondary buttons, recessed tracks (SegmentedToggle), field
|
|
26
|
+
* chrome (FieldContainer/InputField). Adapts to whatever surface sits under it. */
|
|
27
|
+
recessed: 'rgba(0, 0, 0, 0.10)',
|
|
28
|
+
/** Pressed state for a `recessed`-filled surface — a deliberately tuned darker translucent
|
|
29
|
+
* value, not `interaction.pressed` stacked on top of `recessed`: two independently-chosen
|
|
30
|
+
* overlays compositing together produce whatever their alpha math happens to yield, not a
|
|
31
|
+
* value anyone actually looked at and picked. Every `recessed` surface's pressed state should
|
|
32
|
+
* point here instead, so it's one tuned, reusable, nameable value. */
|
|
33
|
+
recessedPressed: 'rgba(0, 0, 0, 0.20)',
|
|
34
|
+
/** Disabled state for a `recessed`-filled surface — same reasoning as `recessedPressed`: a
|
|
35
|
+
* deliberately tuned lighter translucent value (reads as washed-out/inactive) rather than a
|
|
36
|
+
* second overlay stacked on top. */
|
|
37
|
+
recessedDisabled: 'rgba(0, 0, 0, 0.05)',
|
|
38
|
+
},
|
|
39
|
+
text: {
|
|
40
|
+
regular: grey[800],
|
|
41
|
+
/** Secondary/muted text. grey[500] alone falls just short of WCAG AA (4.5:1) for 12–14px text on
|
|
42
|
+
* the surface.main background (~4.34:1) — grey[600] is the next step up and clears it
|
|
43
|
+
* comfortably (~9.5:1), so every semantic colour traces to the palette. */
|
|
44
|
+
muted: grey[600],
|
|
45
|
+
/** Disabled field/value text — a step lighter than `muted` (which doubles as hint/placeholder
|
|
46
|
+
* text), so a disabled field's text doesn't just read as an empty hint. WCAG contrast isn't
|
|
47
|
+
* required for disabled UI (1.4.3 exempts inactive components), so this deliberately sits
|
|
48
|
+
* below the step `muted` was tuned to clear. */
|
|
49
|
+
disabled: grey[500],
|
|
50
|
+
inverse: grey[0],
|
|
51
|
+
/** Inline text links — same value as emphasis.info, so a link and an "info" status read as the
|
|
52
|
+
* same accent, but named for what it's actually used for (Banner's `link` prop) rather than
|
|
53
|
+
* reusing the status-emphasis name at every call site. Clears WCAG AA (~5.6:1) against white
|
|
54
|
+
* and every `shade.*` tint a link might sit on (all near-white). */
|
|
55
|
+
link: blue[600],
|
|
56
|
+
},
|
|
57
|
+
element: {
|
|
58
|
+
divider: 'rgba(0, 0, 0, 0.12)',
|
|
59
|
+
overlayBackdrop: 'rgba(0, 0, 0, 0.34)',
|
|
60
|
+
},
|
|
61
|
+
interaction: {
|
|
62
|
+
/** Universal pressed overlay — apply on top of any light surface. */
|
|
63
|
+
pressed: 'rgba(0, 0, 0, 0.08)',
|
|
64
|
+
/** Lighter hover overlay (web). */
|
|
65
|
+
hover: 'rgba(0, 0, 0, 0.04)',
|
|
66
|
+
/** 2px focus ring color — visible on light surfaces. */
|
|
67
|
+
focused: grey[800],
|
|
68
|
+
/** Disabled opacity — apply to the whole tappable element. */
|
|
69
|
+
disabledOpacity: 0.38,
|
|
70
|
+
},
|
|
71
|
+
border: {
|
|
72
|
+
dark: grey[700],
|
|
73
|
+
light: grey[400],
|
|
74
|
+
/** Subtle UI separators — between divider opacity and border.light. */
|
|
75
|
+
subtle: grey[300],
|
|
76
|
+
},
|
|
77
|
+
/** Saturated accents for text/icons that must read as a status. Each one is set to the step that
|
|
78
|
+
* clears WCAG AA (4.5:1) against its matching `shade` background — `positive`/`warning` need step
|
|
79
|
+
* 700 for that (green/yellow are lighter hues than red/blue at the same step, so 600 falls short:
|
|
80
|
+
* ~4.39:1 and ~3.0:1 respectively); `negative`/`info` already clear it at 600. */
|
|
81
|
+
emphasis: {
|
|
82
|
+
neutral: grey[600],
|
|
83
|
+
positive: green[700],
|
|
84
|
+
warning: yellow[700],
|
|
85
|
+
negative: red[600],
|
|
86
|
+
info: blue[600],
|
|
87
|
+
accent: purple[700],
|
|
88
|
+
},
|
|
89
|
+
/** Light tinted fills for badges/banners/cards of each status. */
|
|
90
|
+
shade: {
|
|
91
|
+
neutral: grey[0],
|
|
92
|
+
positive: green[0],
|
|
93
|
+
warning: yellow[0],
|
|
94
|
+
negative: red[0],
|
|
95
|
+
info: blue[0],
|
|
96
|
+
accent: purple[100],
|
|
97
|
+
},
|
|
98
|
+
} as const;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Elevation tokens as React Native style objects (shadowColor/Offset/Opacity/Radius + Android elevation). */
|
|
2
|
+
export const DS_SHADOW = {
|
|
3
|
+
/** Resting lift for cards / unselected pills — barely there. */
|
|
4
|
+
resting: {
|
|
5
|
+
shadowColor: '#000000',
|
|
6
|
+
shadowOffset: { width: 0, height: 0 },
|
|
7
|
+
shadowOpacity: 0.02,
|
|
8
|
+
shadowRadius: 4,
|
|
9
|
+
elevation: 1,
|
|
10
|
+
},
|
|
11
|
+
/** Gentle drop shadow for card surfaces. */
|
|
12
|
+
card: {
|
|
13
|
+
shadowColor: '#000000',
|
|
14
|
+
shadowOffset: { width: 0, height: 2 },
|
|
15
|
+
shadowOpacity: 0.08,
|
|
16
|
+
shadowRadius: 8,
|
|
17
|
+
elevation: 3,
|
|
18
|
+
},
|
|
19
|
+
/** Upward-cast shadow for bottom sheets — casts above the sheet edge. */
|
|
20
|
+
bottomSheet: {
|
|
21
|
+
shadowColor: '#000000',
|
|
22
|
+
shadowOffset: { width: 0, height: -4 },
|
|
23
|
+
shadowOpacity: 0.12,
|
|
24
|
+
shadowRadius: 12,
|
|
25
|
+
elevation: 8,
|
|
26
|
+
},
|
|
27
|
+
} as const;
|
|
28
|
+
|
|
29
|
+
export type ShadowToken = keyof typeof DS_SHADOW;
|
|
30
|
+
|
|
31
|
+
/** When to reach for each shadow — grounded in real usage across native/components. */
|
|
32
|
+
export const DS_SHADOW_USE: Record<ShadowToken, string> = {
|
|
33
|
+
resting: 'Barely-there lift for surfaces that sit at rest on the page — Card, Pill, SegmentedToggle. Reach for this first.',
|
|
34
|
+
card: "A stronger, deliberate lift for a surface that floats above the page — Toast's bar.",
|
|
35
|
+
bottomSheet: 'An upward-cast shadow (offset negative, casting above the element) for anything pinned to the bottom edge — a bottom sheet or Dock.',
|
|
36
|
+
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type scale + font weights.
|
|
3
|
+
*
|
|
4
|
+
* DS_TYPOGRAPHY entries are RN-shaped (fontSize/fontWeight/lineHeight numbers). On web the same
|
|
5
|
+
* numbers apply as `font-size`/`font-weight`/`line-height` (px). Naming: `label*` are semibold UI
|
|
6
|
+
* labels, `body*` are regular reading text, `emphasis*`/`display`/`title` are headline weights.
|
|
7
|
+
*/
|
|
8
|
+
export const DS_FONT_WEIGHT = {
|
|
9
|
+
normal: '400' as const,
|
|
10
|
+
medium: '500' as const,
|
|
11
|
+
semibold: '600' as const,
|
|
12
|
+
bold: '700' as const,
|
|
13
|
+
extrabold: '800' as const,
|
|
14
|
+
black: '900' as const,
|
|
15
|
+
};
|
|
16
|
+
export type FontWeightName = keyof typeof DS_FONT_WEIGHT;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* When to reach for each font weight — grounded in real usage. Most components get their weight
|
|
20
|
+
* via a spread `DS_TYPOGRAPHY` token (`label*` tokens embed `semibold`, `emphasis*`/`display`/`title`
|
|
21
|
+
* embed `bold`) rather than referencing `DS_FONT_WEIGHT` directly — noted below where that's the case.
|
|
22
|
+
*/
|
|
23
|
+
export const DS_FONT_WEIGHT_USE: Record<FontWeightName, string> = {
|
|
24
|
+
normal: "Regular body text — every DS_TYPOGRAPHY `body*` token embeds this; rarely referenced directly.",
|
|
25
|
+
medium: "A touch heavier than body text without reading as a full label — Toast's message text.",
|
|
26
|
+
semibold: "Standard UI label weight — Button's label, SectionHeader's title; embedded in every DS_TYPOGRAPHY `label*` token.",
|
|
27
|
+
bold: "Headline weight — embedded in every DS_TYPOGRAPHY `emphasis*`/`display`/`title` token; no component references it directly outside those.",
|
|
28
|
+
extrabold: 'Reserved — no current consumer.',
|
|
29
|
+
black: 'Reserved — no current consumer.',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// Every token now carries an explicit lineHeight — before this, labelXs/Sm/Md and emphasisSm/Md/
|
|
33
|
+
// display/title fell back to each platform's own implicit default, which differs between iOS,
|
|
34
|
+
// Android, and web and was a real source of inconsistent vertical rhythm. Two rules set the values:
|
|
35
|
+
// - label* matches its same-fontSize body* counterpart (12→16, 14→20, 16→22) — a label and body
|
|
36
|
+
// line sitting next to each other should land on the same baseline grid regardless of weight.
|
|
37
|
+
// - emphasis*/display/title (no body-scale equivalent to borrow from) extend emphasisLg's own
|
|
38
|
+
// already-established +8 relationship (48→56).
|
|
39
|
+
export const DS_TYPOGRAPHY = {
|
|
40
|
+
labelXs: { fontSize: 12, fontWeight: '600' as const, lineHeight: 16 },
|
|
41
|
+
labelSm: { fontSize: 14, fontWeight: '600' as const, lineHeight: 20 },
|
|
42
|
+
labelMd: { fontSize: 16, fontWeight: '600' as const, lineHeight: 22 },
|
|
43
|
+
bodyXs: { fontSize: 12, fontWeight: '400' as const, lineHeight: 16 },
|
|
44
|
+
bodySm: { fontSize: 14, fontWeight: '400' as const, lineHeight: 20 },
|
|
45
|
+
bodyMd: { fontSize: 16, fontWeight: '400' as const, lineHeight: 22 },
|
|
46
|
+
emphasisSm: { fontSize: 32, fontWeight: '700' as const, lineHeight: 40 },
|
|
47
|
+
emphasisMd: { fontSize: 40, fontWeight: '700' as const, lineHeight: 48 },
|
|
48
|
+
emphasisLg: { fontSize: 48, fontWeight: '700' as const, lineHeight: 56 },
|
|
49
|
+
display: { fontSize: 88, fontWeight: '700' as const, lineHeight: 96 },
|
|
50
|
+
title: { fontSize: 24, fontWeight: '700' as const, lineHeight: 32 },
|
|
51
|
+
} as const;
|
|
52
|
+
|
|
53
|
+
export type TypographyToken = keyof typeof DS_TYPOGRAPHY;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* When to reach for each type token — grounded in how the source app actually uses them, not
|
|
57
|
+
* aspirational. Meant to be read by whoever (human or AI) is deciding which token fits a new piece
|
|
58
|
+
* of UI text; also rendered in the catalog's Typography page.
|
|
59
|
+
*/
|
|
60
|
+
export const DS_TYPOGRAPHY_USE: Record<TypographyToken, string> = {
|
|
61
|
+
labelXs: "Smallest UI label — tab labels, a status row's label, a section header's trailing button text.",
|
|
62
|
+
labelSm: "Small UI label — a pill's text, a small button's label, a banner's title.",
|
|
63
|
+
labelMd: "Standard UI label — a large button's label, an emphasized banner title.",
|
|
64
|
+
bodyXs: "Fine print / secondary meta text — settings captions, a bug-report hint, a train-info row's footnote.",
|
|
65
|
+
bodySm: "Small reading text — a toast's message, a text area's input, a banner's description.",
|
|
66
|
+
bodyMd: "Standard reading text — a search field's input, an input field's value, a screen's body copy.",
|
|
67
|
+
emphasisSm: "A prominent standalone number — a train countdown, a success sheet's headline number.",
|
|
68
|
+
emphasisMd: 'A bigger emphasis number/headline — onboarding hero screens, a key stat on a saved/map screen.',
|
|
69
|
+
emphasisLg: "The largest 'big number' emphasis — an onboarding welcome hero, a live arrival countdown.",
|
|
70
|
+
display: 'Reserved for a true hero/display moment — the largest size on the scale; no current consumer.',
|
|
71
|
+
title: "A screen/page title — a settings screen's heading, an error state's title.",
|
|
72
|
+
};
|