@titan-design/react-ui 0.1.0 → 0.2.5

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.
Files changed (236) hide show
  1. package/README.md +47 -1
  2. package/dist/{chunk-MGW37MPO.mjs → chunk-UXVRPOME.mjs} +173 -3
  3. package/dist/chunk-UXVRPOME.mjs.map +1 -0
  4. package/dist/index.d.mts +101 -12
  5. package/dist/index.d.ts +101 -12
  6. package/dist/index.js +1267 -468
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +734 -112
  9. package/dist/index.mjs.map +1 -1
  10. package/dist/theme/index.d.mts +86 -1
  11. package/dist/theme/index.d.ts +86 -1
  12. package/dist/theme/index.js +166 -0
  13. package/dist/theme/index.js.map +1 -1
  14. package/dist/theme/index.mjs +1 -1
  15. package/package.json +22 -6
  16. package/src/components/custom/DateTime/DateTime.stories.tsx +180 -0
  17. package/src/components/custom/DateTime/DateTime.test.tsx +152 -0
  18. package/src/components/custom/DateTime/DateTime.tsx +235 -0
  19. package/src/components/custom/DateTime/index.ts +2 -0
  20. package/src/components/custom/EmptyState/EmptyState.stories.tsx +99 -0
  21. package/src/components/custom/EmptyState/EmptyState.test.tsx +106 -0
  22. package/src/components/custom/EmptyState/EmptyState.tsx +68 -0
  23. package/src/components/custom/EmptyState/index.ts +2 -0
  24. package/src/components/custom/Metric/Metric.stories.tsx +133 -0
  25. package/src/components/custom/Metric/Metric.test.tsx +120 -0
  26. package/src/components/custom/Metric/Metric.tsx +92 -0
  27. package/src/components/custom/Metric/index.ts +2 -0
  28. package/src/components/custom/Sidebar/Sidebar.stories.tsx +227 -0
  29. package/src/components/custom/Sidebar/Sidebar.test.tsx +185 -0
  30. package/src/components/custom/Sidebar/Sidebar.tsx +257 -0
  31. package/src/components/custom/Sidebar/index.ts +18 -0
  32. package/src/components/custom/Table/Table.stories.tsx +481 -0
  33. package/src/components/custom/Table/Table.test.tsx +531 -0
  34. package/src/components/custom/Table/Table.tsx +696 -0
  35. package/src/components/custom/Table/index.ts +22 -0
  36. package/src/components/custom/Typography/Typography.stories.tsx +153 -0
  37. package/src/components/custom/Typography/Typography.test.tsx +107 -0
  38. package/src/components/custom/Typography/Typography.tsx +218 -0
  39. package/src/components/custom/Typography/index.ts +19 -0
  40. package/src/components/custom/Workout/RestTimer.stories.tsx +71 -0
  41. package/src/components/custom/Workout/RestTimer.test.tsx +145 -0
  42. package/src/components/custom/Workout/RestTimer.tsx +172 -0
  43. package/src/components/custom/Workout/SupersetWrapper.stories.tsx +144 -0
  44. package/src/components/custom/Workout/SupersetWrapper.test.tsx +130 -0
  45. package/src/components/custom/Workout/SupersetWrapper.tsx +63 -0
  46. package/src/components/custom/Workout/index.ts +2 -0
  47. package/src/components/custom/index.ts +9 -0
  48. package/src/components/custom/stepper/Stepper.stories.tsx +245 -0
  49. package/src/components/custom/stepper/Stepper.test.tsx +230 -0
  50. package/src/components/custom/stepper/Stepper.tsx +250 -0
  51. package/src/components/custom/stepper/index.ts +10 -0
  52. package/src/components/index.ts +5 -0
  53. package/src/components/ui/alert/Alert.stories.tsx +154 -0
  54. package/src/components/ui/alert/Alert.test.tsx +202 -0
  55. package/src/components/ui/alert/Alert.tsx +170 -0
  56. package/src/components/ui/alert/index.ts +8 -0
  57. package/src/components/ui/autocomplete/Autocomplete.stories.tsx +299 -0
  58. package/src/components/ui/autocomplete/Autocomplete.test.tsx +247 -0
  59. package/src/components/ui/autocomplete/Autocomplete.tsx +332 -0
  60. package/src/components/ui/autocomplete/index.ts +2 -0
  61. package/src/components/ui/avatar/Avatar.stories.tsx +105 -0
  62. package/src/components/ui/avatar/Avatar.test.tsx +152 -0
  63. package/src/components/ui/avatar/Avatar.tsx +176 -0
  64. package/src/components/ui/avatar/index.ts +2 -0
  65. package/src/components/ui/badge/Badge.stories.tsx +100 -0
  66. package/src/components/ui/badge/Badge.test.tsx +103 -0
  67. package/src/components/ui/badge/Badge.tsx +119 -0
  68. package/src/components/ui/badge/index.ts +3 -0
  69. package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +70 -0
  70. package/src/components/ui/breadcrumbs/Breadcrumbs.test.tsx +156 -0
  71. package/src/components/ui/breadcrumbs/Breadcrumbs.tsx +127 -0
  72. package/src/components/ui/breadcrumbs/index.ts +2 -0
  73. package/src/components/ui/button/Button.stories.tsx +216 -0
  74. package/src/components/ui/button/Button.test.tsx +132 -0
  75. package/src/components/ui/button/Button.tsx +297 -0
  76. package/src/components/ui/button/index.ts +10 -0
  77. package/src/components/ui/card/Card.stories.tsx +443 -0
  78. package/src/components/ui/card/Card.test.tsx +294 -0
  79. package/src/components/ui/card/Card.tsx +401 -0
  80. package/src/components/ui/card/index.ts +20 -0
  81. package/src/components/ui/checkbox/Checkbox.stories.tsx +126 -0
  82. package/src/components/ui/checkbox/Checkbox.test.tsx +152 -0
  83. package/src/components/ui/checkbox/Checkbox.tsx +163 -0
  84. package/src/components/ui/checkbox/index.ts +2 -0
  85. package/src/components/ui/chip/Chip.stories.tsx +112 -0
  86. package/src/components/ui/chip/Chip.test.tsx +119 -0
  87. package/src/components/ui/chip/Chip.tsx +151 -0
  88. package/src/components/ui/chip/index.ts +2 -0
  89. package/src/components/ui/collapse/Collapse.stories.tsx +313 -0
  90. package/src/components/ui/collapse/Collapse.test.tsx +298 -0
  91. package/src/components/ui/collapse/Collapse.tsx +275 -0
  92. package/src/components/ui/collapse/index.ts +18 -0
  93. package/src/components/ui/data-row/DataRow.stories.tsx +78 -0
  94. package/src/components/ui/data-row/DataRow.test.tsx +78 -0
  95. package/src/components/ui/data-row/DataRow.tsx +29 -0
  96. package/src/components/ui/data-row/index.ts +2 -0
  97. package/src/components/ui/divider/Divider.stories.tsx +71 -0
  98. package/src/components/ui/divider/Divider.test.tsx +55 -0
  99. package/src/components/ui/divider/Divider.tsx +37 -0
  100. package/src/components/ui/divider/index.ts +2 -0
  101. package/src/components/ui/drawer/Drawer.stories.tsx +228 -0
  102. package/src/components/ui/drawer/Drawer.test.tsx +142 -0
  103. package/src/components/ui/drawer/Drawer.tsx +228 -0
  104. package/src/components/ui/drawer/index.ts +9 -0
  105. package/src/components/ui/form-field/FormField.stories.tsx +260 -0
  106. package/src/components/ui/form-field/FormField.test.tsx +280 -0
  107. package/src/components/ui/form-field/FormField.tsx +308 -0
  108. package/src/components/ui/form-field/index.ts +2 -0
  109. package/src/components/ui/help-tip/HelpTip.stories.tsx +250 -0
  110. package/src/components/ui/help-tip/HelpTip.test.tsx +198 -0
  111. package/src/components/ui/help-tip/HelpTip.tsx +229 -0
  112. package/src/components/ui/help-tip/index.ts +2 -0
  113. package/src/components/ui/icon-box/IconBox.stories.tsx +139 -0
  114. package/src/components/ui/icon-box/IconBox.test.tsx +120 -0
  115. package/src/components/ui/icon-box/IconBox.tsx +57 -0
  116. package/src/components/ui/icon-box/index.ts +1 -0
  117. package/src/components/ui/index.ts +38 -0
  118. package/src/components/ui/indicator/Indicator.stories.tsx +74 -0
  119. package/src/components/ui/indicator/Indicator.test.tsx +55 -0
  120. package/src/components/ui/indicator/Indicator.tsx +73 -0
  121. package/src/components/ui/indicator/index.ts +2 -0
  122. package/src/components/ui/input/Input.stories.tsx +192 -0
  123. package/src/components/ui/input/Input.test.tsx +86 -0
  124. package/src/components/ui/input/Input.tsx +273 -0
  125. package/src/components/ui/input/index.ts +2 -0
  126. package/src/components/ui/link/Link.stories.tsx +78 -0
  127. package/src/components/ui/link/Link.test.tsx +94 -0
  128. package/src/components/ui/link/Link.tsx +86 -0
  129. package/src/components/ui/link/index.ts +2 -0
  130. package/src/components/ui/list-item/ListItem.stories.tsx +146 -0
  131. package/src/components/ui/list-item/ListItem.test.tsx +128 -0
  132. package/src/components/ui/list-item/ListItem.tsx +89 -0
  133. package/src/components/ui/list-item/index.ts +14 -0
  134. package/src/components/ui/menu/Menu.stories.tsx +180 -0
  135. package/src/components/ui/menu/Menu.test.tsx +353 -0
  136. package/src/components/ui/menu/Menu.tsx +222 -0
  137. package/src/components/ui/menu/index.ts +2 -0
  138. package/src/components/ui/modal/Modal.stories.tsx +350 -0
  139. package/src/components/ui/modal/Modal.test.tsx +159 -0
  140. package/src/components/ui/modal/Modal.tsx +254 -0
  141. package/src/components/ui/modal/index.ts +19 -0
  142. package/src/components/ui/pill/Pill.stories.tsx +71 -0
  143. package/src/components/ui/pill/Pill.test.tsx +69 -0
  144. package/src/components/ui/pill/Pill.tsx +104 -0
  145. package/src/components/ui/pill/index.ts +2 -0
  146. package/src/components/ui/popover/Popover.stories.tsx +190 -0
  147. package/src/components/ui/popover/Popover.test.tsx +419 -0
  148. package/src/components/ui/popover/Popover.tsx +237 -0
  149. package/src/components/ui/popover/index.ts +2 -0
  150. package/src/components/ui/progress/Progress.stories.tsx +190 -0
  151. package/src/components/ui/progress/Progress.test.tsx +210 -0
  152. package/src/components/ui/progress/Progress.tsx +339 -0
  153. package/src/components/ui/progress/index.ts +9 -0
  154. package/src/components/ui/radio/Radio.stories.tsx +199 -0
  155. package/src/components/ui/radio/Radio.test.tsx +166 -0
  156. package/src/components/ui/radio/Radio.tsx +200 -0
  157. package/src/components/ui/radio/index.ts +2 -0
  158. package/src/components/ui/section/Section.stories.tsx +111 -0
  159. package/src/components/ui/section/Section.test.tsx +121 -0
  160. package/src/components/ui/section/Section.tsx +52 -0
  161. package/src/components/ui/section/index.ts +6 -0
  162. package/src/components/ui/select/Select.stories.tsx +286 -0
  163. package/src/components/ui/select/Select.test.tsx +200 -0
  164. package/src/components/ui/select/Select.tsx +243 -0
  165. package/src/components/ui/select/index.ts +2 -0
  166. package/src/components/ui/skeleton/Skeleton.stories.tsx +153 -0
  167. package/src/components/ui/skeleton/Skeleton.test.tsx +162 -0
  168. package/src/components/ui/skeleton/Skeleton.tsx +229 -0
  169. package/src/components/ui/skeleton/index.ts +16 -0
  170. package/src/components/ui/spinner/Spinner.stories.tsx +67 -0
  171. package/src/components/ui/spinner/Spinner.test.tsx +67 -0
  172. package/src/components/ui/spinner/Spinner.tsx +72 -0
  173. package/src/components/ui/spinner/index.ts +2 -0
  174. package/src/components/ui/stack/Stack.stories.tsx +127 -0
  175. package/src/components/ui/stack/Stack.test.tsx +184 -0
  176. package/src/components/ui/stack/Stack.tsx +76 -0
  177. package/src/components/ui/stack/index.ts +2 -0
  178. package/src/components/ui/surface/Surface.stories.tsx +91 -0
  179. package/src/components/ui/surface/Surface.test.tsx +63 -0
  180. package/src/components/ui/surface/Surface.tsx +51 -0
  181. package/src/components/ui/surface/index.ts +1 -0
  182. package/src/components/ui/switch/Switch.stories.tsx +111 -0
  183. package/src/components/ui/switch/Switch.test.tsx +112 -0
  184. package/src/components/ui/switch/Switch.tsx +103 -0
  185. package/src/components/ui/switch/index.ts +2 -0
  186. package/src/components/ui/tabs/Tabs.stories.tsx +213 -0
  187. package/src/components/ui/tabs/Tabs.test.tsx +192 -0
  188. package/src/components/ui/tabs/Tabs.tsx +253 -0
  189. package/src/components/ui/tabs/index.ts +16 -0
  190. package/src/components/ui/toast/Toast.stories.tsx +255 -0
  191. package/src/components/ui/toast/Toast.test.tsx +207 -0
  192. package/src/components/ui/toast/Toast.tsx +327 -0
  193. package/src/components/ui/toast/index.ts +9 -0
  194. package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +415 -0
  195. package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +224 -0
  196. package/src/components/ui/toolbar-button/ToolbarButton.tsx +307 -0
  197. package/src/components/ui/toolbar-button/index.ts +7 -0
  198. package/src/components/ui/tooltip/Tooltip.stories.tsx +126 -0
  199. package/src/components/ui/tooltip/Tooltip.test.tsx +262 -0
  200. package/src/components/ui/tooltip/Tooltip.tsx +234 -0
  201. package/src/components/ui/tooltip/index.ts +2 -0
  202. package/src/index.ts +10 -0
  203. package/src/stories/PresetShowcase.stories.tsx +462 -0
  204. package/src/stories/ThemePresets.stories.tsx +85 -0
  205. package/src/test/setup.ts +19 -0
  206. package/src/theme/Colors.stories.tsx +258 -0
  207. package/src/theme/config.ts +193 -0
  208. package/src/theme/elevation.stories.tsx +346 -0
  209. package/src/theme/elevation.test.ts +27 -0
  210. package/src/theme/elevation.ts +567 -0
  211. package/src/theme/global.css +129 -1
  212. package/src/theme/index.ts +40 -0
  213. package/src/theme/presets/apply.ts +97 -0
  214. package/src/theme/presets/audiobook.ts +93 -0
  215. package/src/theme/presets/default.ts +6 -0
  216. package/src/theme/presets/index.ts +4 -0
  217. package/src/theme/presets/presets.test.ts +51 -0
  218. package/src/theme/presets/types.ts +76 -0
  219. package/src/theme/shadows.stories.tsx +523 -0
  220. package/src/theme/shadows.ts +439 -0
  221. package/src/theme/tokens/index.ts +2 -0
  222. package/src/theme/tokens/primitives.ts +354 -0
  223. package/src/theme/tokens/semantic.ts +381 -0
  224. package/src/types/jest-axe.d.ts +16 -0
  225. package/src/types/nativewind.d.ts +54 -0
  226. package/src/utils/avatar-color.test.ts +30 -0
  227. package/src/utils/avatar-color.ts +20 -0
  228. package/src/utils/cn.ts +14 -0
  229. package/src/utils/colors.ts +140 -0
  230. package/src/utils/form.ts +188 -0
  231. package/src/utils/index.ts +29 -0
  232. package/src/utils/useTheme.ts +72 -0
  233. package/src/web-jsx/jsx-dev-runtime.ts +28 -0
  234. package/src/web-jsx/jsx-runtime.ts +41 -0
  235. package/tailwind.config.js +53 -3
  236. package/dist/chunk-MGW37MPO.mjs.map +0 -1
@@ -0,0 +1,462 @@
1
+ import React, { useEffect, useState } from 'react'
2
+ import type { Meta, StoryObj } from '@storybook/react-vite'
3
+ import { View, Text, Pressable } from 'react-native'
4
+ import { Button, ButtonText } from '../components/ui/button'
5
+ import { Card } from '../components/ui/card'
6
+ import { Input } from '../components/ui/input'
7
+ import { Progress, CircularProgress } from '../components/ui/progress'
8
+ import { Badge } from '../components/ui/badge'
9
+ import { Toast } from '../components/ui/toast'
10
+ import { Switch } from '../components/ui/switch'
11
+ import { applyThemePreset, audiobookPreset, defaultPreset } from '../theme/presets'
12
+ import type { ThemePreset } from '../theme/presets'
13
+
14
+ const presets: Record<string, ThemePreset> = {
15
+ default: defaultPreset,
16
+ audiobook: audiobookPreset,
17
+ }
18
+
19
+ function PresetSwitcher({
20
+ activePreset,
21
+ onSwitch,
22
+ }: {
23
+ activePreset: string
24
+ onSwitch: (name: string) => void
25
+ }) {
26
+ return (
27
+ <View className="flex-row items-center gap-3 p-4 bg-surface-elevated rounded-lg border border-border-default">
28
+ <Text className="text-text-secondary text-sm font-medium">Active Preset:</Text>
29
+ {Object.keys(presets).map((name) => (
30
+ <Pressable
31
+ key={name}
32
+ onPress={() => onSwitch(name)}
33
+ className={`px-4 py-2 rounded-md border ${
34
+ activePreset === name
35
+ ? 'bg-brand-primary border-brand-primary'
36
+ : 'bg-surface-base border-border-default web:hover:bg-interactive-hover'
37
+ }`}
38
+ >
39
+ <Text
40
+ className={`text-sm font-semibold capitalize ${
41
+ activePreset === name ? 'text-on-brand-primary' : 'text-text-primary'
42
+ }`}
43
+ >
44
+ {name}
45
+ </Text>
46
+ </Pressable>
47
+ ))}
48
+ </View>
49
+ )
50
+ }
51
+
52
+ function usePresetToggle(initial = 'default') {
53
+ const [activePreset, setActivePreset] = useState(initial)
54
+
55
+ useEffect(() => {
56
+ const cleanup = applyThemePreset(presets[activePreset])
57
+ return cleanup
58
+ }, [activePreset])
59
+
60
+ return { activePreset, setActivePreset }
61
+ }
62
+
63
+ // ---------------------------------------------------------------------------
64
+ // SideBySide Story
65
+ // ---------------------------------------------------------------------------
66
+
67
+ function SideBySideShowcase() {
68
+ const { activePreset, setActivePreset } = usePresetToggle()
69
+
70
+ return (
71
+ <View className="gap-6 p-6 bg-background-default min-h-screen">
72
+ <View className="gap-2">
73
+ <Text className="text-3xl font-bold text-text-primary font-heading">
74
+ Preset Showcase
75
+ </Text>
76
+ <Text className="text-text-secondary text-base">
77
+ Toggle between presets to see how the entire design system adapts.
78
+ </Text>
79
+ </View>
80
+
81
+ <PresetSwitcher activePreset={activePreset} onSwitch={setActivePreset} />
82
+
83
+ {/* Typography */}
84
+ <Section title="Typography">
85
+ <Text className="text-3xl font-bold text-text-primary font-heading">
86
+ Heading (font-heading)
87
+ </Text>
88
+ <Text className="text-xl font-semibold text-text-primary font-heading">
89
+ Subheading (font-heading)
90
+ </Text>
91
+ <Text className="text-base text-text-primary font-body">
92
+ Body text using font-body. The quick brown fox jumps over the lazy dog. This
93
+ demonstrates how paragraph text renders under the current preset.
94
+ </Text>
95
+ <Text className="text-sm text-text-secondary font-body">
96
+ Secondary body text for captions and supporting content.
97
+ </Text>
98
+ <Text className="text-sm text-text-primary font-mono">
99
+ {'const monospace = \'font-mono rendering\''}
100
+ </Text>
101
+ </Section>
102
+
103
+ {/* Buttons */}
104
+ <Section title="Buttons">
105
+ <View className="gap-3">
106
+ <Text className="text-xs font-semibold text-text-tertiary uppercase tracking-wider">
107
+ Primary
108
+ </Text>
109
+ <View className="flex-row gap-3 flex-wrap">
110
+ <Button variant="solid" color="primary">
111
+ <ButtonText>Solid</ButtonText>
112
+ </Button>
113
+ <Button variant="outline" color="primary">
114
+ <ButtonText>Outline</ButtonText>
115
+ </Button>
116
+ <Button variant="ghost" color="primary">
117
+ <ButtonText>Ghost</ButtonText>
118
+ </Button>
119
+ </View>
120
+ <Text className="text-xs font-semibold text-text-tertiary uppercase tracking-wider">
121
+ Secondary
122
+ </Text>
123
+ <View className="flex-row gap-3 flex-wrap">
124
+ <Button variant="solid" color="secondary">
125
+ <ButtonText>Solid</ButtonText>
126
+ </Button>
127
+ <Button variant="outline" color="secondary">
128
+ <ButtonText>Outline</ButtonText>
129
+ </Button>
130
+ <Button variant="ghost" color="secondary">
131
+ <ButtonText>Ghost</ButtonText>
132
+ </Button>
133
+ </View>
134
+ </View>
135
+ </Section>
136
+
137
+ {/* Cards with Badges */}
138
+ <Section title="Cards">
139
+ <View className="flex-row gap-4 flex-wrap">
140
+ <Card className="p-4 gap-3 flex-1 min-w-[260px]">
141
+ <Text className="text-lg font-semibold text-text-primary font-heading">
142
+ Elevated Card
143
+ </Text>
144
+ <Text className="text-text-secondary text-sm">
145
+ Surface elevation and border colors adapt to the active preset.
146
+ </Text>
147
+ <View className="flex-row gap-2 flex-wrap">
148
+ <Badge color="success">Complete</Badge>
149
+ <Badge color="warning">Pending</Badge>
150
+ <Badge color="error">Failed</Badge>
151
+ </View>
152
+ </Card>
153
+ <Card className="p-4 gap-3 flex-1 min-w-[260px]">
154
+ <Text className="text-lg font-semibold text-text-primary font-heading">
155
+ Content Card
156
+ </Text>
157
+ <Text className="text-text-secondary text-sm">
158
+ Brand secondary and info badges shown below for comparison.
159
+ </Text>
160
+ <View className="flex-row gap-2 flex-wrap">
161
+ <Badge color="info">Info</Badge>
162
+ <Badge color="secondary">Secondary</Badge>
163
+ </View>
164
+ </Card>
165
+ </View>
166
+ </Section>
167
+
168
+ {/* Input + Switch */}
169
+ <Section title="Inputs">
170
+ <View className="gap-3 max-w-[400px]">
171
+ <Input placeholder="Default input..." />
172
+ <Input placeholder="Disabled input..." isDisabled />
173
+ <View className="flex-row items-center gap-3">
174
+ <Switch />
175
+ <Text className="text-text-primary text-sm">Toggle switch</Text>
176
+ </View>
177
+ </View>
178
+ </Section>
179
+
180
+ {/* Progress */}
181
+ <Section title="Progress">
182
+ <View className="gap-4">
183
+ <Progress value={65} label="Primary" showValue color="primary" />
184
+ <Progress value={40} label="Secondary" showValue color="secondary" />
185
+ <Progress value={100} label="Success" showValue color="success" />
186
+ <View className="flex-row gap-4 items-center flex-wrap">
187
+ <CircularProgress value={25} size={48} showValue color="primary" />
188
+ <CircularProgress value={50} size={48} showValue color="secondary" />
189
+ <CircularProgress value={75} size={48} showValue color="success" />
190
+ <CircularProgress value={100} size={48} showValue color="error" />
191
+ </View>
192
+ </View>
193
+ </Section>
194
+
195
+ {/* Toasts */}
196
+ <Section title="Toasts">
197
+ <View className="gap-3">
198
+ <Toast title="Operation successful" description="Your changes have been saved." status="success" />
199
+ <Toast title="Something went wrong" description="Please try again later." status="error" />
200
+ <Toast title="Heads up" description="This action cannot be undone." status="warning" />
201
+ <Toast title="New update available" description="Version 2.0 is ready to install." status="info" />
202
+ </View>
203
+ </Section>
204
+
205
+ {/* Status Colors */}
206
+ <Section title="Status Colors">
207
+ <View className="flex-row gap-3 flex-wrap">
208
+ <Badge color="success" size="lg">Success</Badge>
209
+ <Badge color="error" size="lg">Error</Badge>
210
+ <Badge color="warning" size="lg">Warning</Badge>
211
+ <Badge color="info" size="lg">Info</Badge>
212
+ </View>
213
+ </Section>
214
+ </View>
215
+ )
216
+ }
217
+
218
+ // ---------------------------------------------------------------------------
219
+ // Typography Story
220
+ // ---------------------------------------------------------------------------
221
+
222
+ function TypographyShowcase() {
223
+ const { activePreset, setActivePreset } = usePresetToggle()
224
+
225
+ return (
226
+ <View className="gap-6 p-6 bg-background-default min-h-screen">
227
+ <View className="gap-2">
228
+ <Text className="text-3xl font-bold text-text-primary font-heading">
229
+ Typography
230
+ </Text>
231
+ <Text className="text-text-secondary text-base">
232
+ How fonts and text styles change across presets.
233
+ </Text>
234
+ </View>
235
+
236
+ <PresetSwitcher activePreset={activePreset} onSwitch={setActivePreset} />
237
+
238
+ <Section title="Heading Font (font-heading)">
239
+ <Text className="text-4xl font-bold text-text-primary font-heading">
240
+ H1 - The Art of Design Systems
241
+ </Text>
242
+ <Text className="text-3xl font-bold text-text-primary font-heading">
243
+ H2 - Component Architecture
244
+ </Text>
245
+ <Text className="text-2xl font-semibold text-text-primary font-heading">
246
+ H3 - Token-Driven Theming
247
+ </Text>
248
+ <Text className="text-xl font-semibold text-text-primary font-heading">
249
+ H4 - Cross-Platform Consistency
250
+ </Text>
251
+ </Section>
252
+
253
+ <Section title="Body Font (font-body)">
254
+ <Text className="text-base text-text-primary font-body">
255
+ Regular body text. Design systems provide a shared language for product teams.
256
+ By codifying decisions into reusable components and tokens, teams move faster
257
+ while maintaining visual consistency across every surface.
258
+ </Text>
259
+ <Text className="text-base font-semibold text-text-primary font-body">
260
+ Bold body text. Emphasis and hierarchy within paragraph content.
261
+ </Text>
262
+ <Text className="text-base italic text-text-primary font-body">
263
+ Italic body text. Used for quotations, citations, and emphasis.
264
+ </Text>
265
+ <Text className="text-sm text-text-secondary font-body">
266
+ Small / secondary text. Captions, labels, and supporting information
267
+ that supplements the main content without competing for attention.
268
+ </Text>
269
+ <Text className="text-xs text-text-tertiary font-body">
270
+ Extra-small / tertiary text. Timestamps, metadata, fine print.
271
+ </Text>
272
+ </Section>
273
+
274
+ <Section title="Monospace Font (font-mono)">
275
+ <View className="bg-surface-elevated rounded-lg p-4 border border-border-default">
276
+ <Text className="text-sm text-text-primary font-mono">
277
+ {'const theme = applyThemePreset(audiobookPreset)'}
278
+ </Text>
279
+ <Text className="text-sm text-text-secondary font-mono">
280
+ {'// Overrides CSS custom properties on :root'}
281
+ </Text>
282
+ <Text className="text-sm text-text-primary font-mono">
283
+ {'export type ThemePreset = { name: string }'}
284
+ </Text>
285
+ </View>
286
+ </Section>
287
+
288
+ <Section title="Mixed Typography">
289
+ <Card className="p-5 gap-3">
290
+ <Text className="text-2xl font-bold text-text-primary font-heading">
291
+ Chapter One
292
+ </Text>
293
+ <Text className="text-base text-text-secondary font-body">
294
+ It was a bright cold day in April, and the clocks were striking thirteen.
295
+ Winston Smith, his chin nuzzled into his breast in an effort to escape the
296
+ vile wind, slipped quickly through the glass doors of Victory Mansions,
297
+ though not quickly enough to prevent a swirl of gritty dust from entering
298
+ along with him.
299
+ </Text>
300
+ <Text className="text-xs text-text-tertiary font-mono">
301
+ George Orwell, 1984
302
+ </Text>
303
+ </Card>
304
+ </Section>
305
+ </View>
306
+ )
307
+ }
308
+
309
+ // ---------------------------------------------------------------------------
310
+ // ColorPalette Story
311
+ // ---------------------------------------------------------------------------
312
+
313
+ function ColorSwatch({
314
+ label,
315
+ className,
316
+ textClass = 'text-white',
317
+ }: {
318
+ label: string
319
+ className: string
320
+ textClass?: string
321
+ }) {
322
+ return (
323
+ <View className={`rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end ${className}`}>
324
+ <Text className={`text-xs font-mono font-medium ${textClass}`}>{label}</Text>
325
+ </View>
326
+ )
327
+ }
328
+
329
+ function ColorPaletteShowcase() {
330
+ const { activePreset, setActivePreset } = usePresetToggle()
331
+
332
+ return (
333
+ <View className="gap-6 p-6 bg-background-default min-h-screen">
334
+ <View className="gap-2">
335
+ <Text className="text-3xl font-bold text-text-primary font-heading">
336
+ Color Palette
337
+ </Text>
338
+ <Text className="text-text-secondary text-base">
339
+ Semantic color tokens rendered as swatches. Toggle presets to compare palettes.
340
+ </Text>
341
+ </View>
342
+
343
+ <PresetSwitcher activePreset={activePreset} onSwitch={setActivePreset} />
344
+
345
+ <Section title="Brand Colors">
346
+ <View className="flex-row gap-3 flex-wrap">
347
+ <ColorSwatch label="brand-primary" className="bg-brand-primary" />
348
+ <ColorSwatch label="brand-primary-light" className="bg-brand-primary-light" />
349
+ <ColorSwatch label="brand-primary-dark" className="bg-brand-primary-dark" />
350
+ <ColorSwatch label="brand-primary-subtle" className="bg-brand-primary-subtle" textClass="text-text-primary" />
351
+ </View>
352
+ <View className="flex-row gap-3 flex-wrap mt-2">
353
+ <ColorSwatch label="brand-secondary" className="bg-brand-secondary" />
354
+ <ColorSwatch label="brand-secondary-light" className="bg-brand-secondary-light" />
355
+ <ColorSwatch label="brand-secondary-dark" className="bg-brand-secondary-dark" />
356
+ <ColorSwatch label="brand-secondary-subtle" className="bg-brand-secondary-subtle" textClass="text-text-primary" />
357
+ </View>
358
+ </Section>
359
+
360
+ <Section title="Status Colors">
361
+ <View className="flex-row gap-3 flex-wrap">
362
+ <ColorSwatch label="status-success" className="bg-status-success" />
363
+ <ColorSwatch label="status-error" className="bg-status-error" />
364
+ <ColorSwatch label="status-warning" className="bg-status-warning" />
365
+ <ColorSwatch label="status-info" className="bg-status-info" />
366
+ </View>
367
+ </Section>
368
+
369
+ <Section title="Text Colors">
370
+ <View className="flex-row gap-3 flex-wrap">
371
+ <View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-elevated border border-border-default">
372
+ <Text className="text-xs font-mono font-medium text-text-primary">text-primary</Text>
373
+ </View>
374
+ <View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-elevated border border-border-default">
375
+ <Text className="text-xs font-mono font-medium text-text-secondary">text-secondary</Text>
376
+ </View>
377
+ <View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-elevated border border-border-default">
378
+ <Text className="text-xs font-mono font-medium text-text-tertiary">text-tertiary</Text>
379
+ </View>
380
+ </View>
381
+ </Section>
382
+
383
+ <Section title="Surface Colors">
384
+ <View className="flex-row gap-3 flex-wrap">
385
+ <ColorSwatch label="surface-base" className="bg-surface-base border border-border-default" textClass="text-text-primary" />
386
+ <ColorSwatch label="surface-elevated" className="bg-surface-elevated border border-border-default" textClass="text-text-primary" />
387
+ <ColorSwatch label="surface-raised" className="bg-surface-raised border border-border-default" textClass="text-text-primary" />
388
+ <ColorSwatch label="surface-overlay" className="bg-surface-overlay border border-border-default" textClass="text-text-primary" />
389
+ <ColorSwatch label="surface-input" className="bg-surface-input border border-border-default" textClass="text-text-primary" />
390
+ </View>
391
+ </Section>
392
+
393
+ <Section title="Background Colors">
394
+ <View className="flex-row gap-3 flex-wrap">
395
+ <ColorSwatch label="background-base" className="bg-background-base border border-border-default" textClass="text-text-primary" />
396
+ <ColorSwatch label="background-default" className="bg-background-default border border-border-default" textClass="text-text-primary" />
397
+ <ColorSwatch label="background-subtle" className="bg-background-subtle border border-border-default" textClass="text-text-primary" />
398
+ </View>
399
+ </Section>
400
+
401
+ <Section title="Border Colors">
402
+ <View className="flex-row gap-3 flex-wrap">
403
+ <View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-base border-2 border-border-default">
404
+ <Text className="text-xs font-mono font-medium text-text-primary">border-default</Text>
405
+ </View>
406
+ <View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-base border-2 border-border-subtle">
407
+ <Text className="text-xs font-mono font-medium text-text-primary">border-subtle</Text>
408
+ </View>
409
+ <View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-base border-2 border-border-strong">
410
+ <Text className="text-xs font-mono font-medium text-text-primary">border-strong</Text>
411
+ </View>
412
+ <View className="rounded-lg p-3 min-w-[120px] min-h-[60px] justify-end bg-surface-base border-2 border-border-focus">
413
+ <Text className="text-xs font-mono font-medium text-text-primary">border-focus</Text>
414
+ </View>
415
+ </View>
416
+ </Section>
417
+ </View>
418
+ )
419
+ }
420
+
421
+ // ---------------------------------------------------------------------------
422
+ // Shared Section Component
423
+ // ---------------------------------------------------------------------------
424
+
425
+ function Section({ title, children }: { title: string; children: React.ReactNode }) {
426
+ return (
427
+ <View className="gap-3">
428
+ <Text className="text-lg font-semibold text-text-primary font-heading border-b border-border-subtle pb-2">
429
+ {title}
430
+ </Text>
431
+ {children}
432
+ </View>
433
+ )
434
+ }
435
+
436
+ // ---------------------------------------------------------------------------
437
+ // Storybook Meta & Exports
438
+ // ---------------------------------------------------------------------------
439
+
440
+ const meta: Meta = {
441
+ title: 'Theme/Preset Showcase',
442
+ parameters: {
443
+ layout: 'fullscreen',
444
+ },
445
+ }
446
+
447
+ export default meta
448
+
449
+ export const SideBySide: StoryObj = {
450
+ render: () => <SideBySideShowcase />,
451
+ name: 'Component Showcase',
452
+ }
453
+
454
+ export const Typography: StoryObj = {
455
+ render: () => <TypographyShowcase />,
456
+ name: 'Typography',
457
+ }
458
+
459
+ export const ColorPalette: StoryObj = {
460
+ render: () => <ColorPaletteShowcase />,
461
+ name: 'Color Palette',
462
+ }
@@ -0,0 +1,85 @@
1
+ import React, { useEffect } from 'react'
2
+ import type { Meta, StoryObj } from '@storybook/react-vite'
3
+ import { View, Text } from 'react-native'
4
+ import { Button, ButtonText } from '../components/ui/button'
5
+ import { Card } from '../components/ui/card'
6
+ import { Input } from '../components/ui/input'
7
+ import { Progress, CircularProgress } from '../components/ui/progress'
8
+ import { Badge } from '../components/ui/badge'
9
+ import { applyThemePreset, audiobookPreset, defaultPreset } from '../theme/presets'
10
+
11
+ function PresetShowcase({ preset }: { preset: typeof audiobookPreset }) {
12
+ useEffect(() => {
13
+ const cleanup = applyThemePreset(preset)
14
+ return cleanup
15
+ }, [preset])
16
+
17
+ return (
18
+ <View className="gap-6 p-6 bg-background-default min-h-screen">
19
+ <Text className="text-2xl font-bold text-text-primary font-heading">
20
+ {preset.name} preset
21
+ </Text>
22
+
23
+ {/* Buttons */}
24
+ <View className="flex-row gap-3 flex-wrap">
25
+ <Button variant="solid" color="primary">
26
+ <ButtonText>Primary</ButtonText>
27
+ </Button>
28
+ <Button variant="solid" color="secondary">
29
+ <ButtonText>Secondary</ButtonText>
30
+ </Button>
31
+ <Button variant="outline" color="primary">
32
+ <ButtonText>Outline</ButtonText>
33
+ </Button>
34
+ <Button variant="ghost" color="primary">
35
+ <ButtonText>Ghost</ButtonText>
36
+ </Button>
37
+ </View>
38
+
39
+ {/* Card */}
40
+ <Card className="p-4 gap-3">
41
+ <Text className="text-lg font-semibold text-text-primary">Sample Card</Text>
42
+ <Text className="text-text-secondary">
43
+ This card demonstrates surface elevation and border colors with the current preset.
44
+ </Text>
45
+ <View className="flex-row gap-2">
46
+ <Badge color="success">Complete</Badge>
47
+ <Badge color="warning">Pending</Badge>
48
+ <Badge color="error">Failed</Badge>
49
+ </View>
50
+ </Card>
51
+
52
+ {/* Input */}
53
+ <Input placeholder="Type something..." />
54
+
55
+ {/* Progress */}
56
+ <View className="gap-3">
57
+ <Progress value={65} label="Processing" showValue color="primary" />
58
+ <View className="flex-row gap-4 items-center">
59
+ <CircularProgress value={25} size={48} showValue color="primary" />
60
+ <CircularProgress value={75} size={48} showValue color="secondary" />
61
+ <CircularProgress value={100} size={48} showValue color="success" />
62
+ </View>
63
+ </View>
64
+ </View>
65
+ )
66
+ }
67
+
68
+ const meta: Meta = {
69
+ title: 'Theme/Presets',
70
+ parameters: {
71
+ layout: 'fullscreen',
72
+ },
73
+ }
74
+
75
+ export default meta
76
+
77
+ export const Default: StoryObj = {
78
+ render: () => <PresetShowcase preset={defaultPreset} />,
79
+ name: 'Default Preset',
80
+ }
81
+
82
+ export const Audiobook: StoryObj = {
83
+ render: () => <PresetShowcase preset={audiobookPreset} />,
84
+ name: 'Audiobook Preset',
85
+ }
@@ -0,0 +1,19 @@
1
+ /// <reference types="vitest/globals" />
2
+ import '@testing-library/jest-dom'
3
+ import { expect, afterEach } from 'vitest'
4
+ import { cleanup } from '@testing-library/react'
5
+ import { toHaveNoViolations } from 'jest-axe'
6
+
7
+ // Extend Vitest's expect with jest-axe matchers
8
+ expect.extend(toHaveNoViolations)
9
+
10
+ // Cleanup after each test
11
+ afterEach(() => {
12
+ cleanup()
13
+ })
14
+
15
+ // Mock react-native components for web testing
16
+ vi.mock('react-native', async () => {
17
+ const RNW = await vi.importActual('react-native-web')
18
+ return RNW
19
+ })