@titan-design/react-ui 0.0.2 → 0.1.1

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 (206) hide show
  1. package/dist/{chunk-NA3EES23.mjs → chunk-MGW37MPO.mjs} +26 -4
  2. package/dist/chunk-MGW37MPO.mjs.map +1 -0
  3. package/dist/index.d.mts +115 -2
  4. package/dist/index.d.ts +115 -2
  5. package/dist/index.js +262 -1
  6. package/dist/index.js.map +1 -1
  7. package/dist/index.mjs +225 -3
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/theme/index.d.mts +124 -109
  10. package/dist/theme/index.d.ts +124 -109
  11. package/dist/theme/index.js +24 -1
  12. package/dist/theme/index.js.map +1 -1
  13. package/dist/theme/index.mjs +1 -1
  14. package/package.json +12 -5
  15. package/src/components/custom/DateTime/DateTime.stories.tsx +180 -0
  16. package/src/components/custom/DateTime/DateTime.test.tsx +152 -0
  17. package/src/components/custom/DateTime/DateTime.tsx +235 -0
  18. package/src/components/custom/DateTime/index.ts +2 -0
  19. package/src/components/custom/EmptyState/EmptyState.stories.tsx +99 -0
  20. package/src/components/custom/EmptyState/EmptyState.test.tsx +106 -0
  21. package/src/components/custom/EmptyState/EmptyState.tsx +68 -0
  22. package/src/components/custom/EmptyState/index.ts +2 -0
  23. package/src/components/custom/Metric/Metric.stories.tsx +133 -0
  24. package/src/components/custom/Metric/Metric.test.tsx +120 -0
  25. package/src/components/custom/Metric/Metric.tsx +92 -0
  26. package/src/components/custom/Metric/index.ts +2 -0
  27. package/src/components/custom/Sidebar/Sidebar.stories.tsx +227 -0
  28. package/src/components/custom/Sidebar/Sidebar.test.tsx +185 -0
  29. package/src/components/custom/Sidebar/Sidebar.tsx +257 -0
  30. package/src/components/custom/Sidebar/index.ts +18 -0
  31. package/src/components/custom/Table/Table.stories.tsx +481 -0
  32. package/src/components/custom/Table/Table.test.tsx +531 -0
  33. package/src/components/custom/Table/Table.tsx +696 -0
  34. package/src/components/custom/Table/index.ts +22 -0
  35. package/src/components/custom/Typography/Typography.stories.tsx +153 -0
  36. package/src/components/custom/Typography/Typography.test.tsx +107 -0
  37. package/src/components/custom/Typography/Typography.tsx +218 -0
  38. package/src/components/custom/Typography/index.ts +19 -0
  39. package/src/components/custom/index.ts +8 -0
  40. package/src/components/custom/stepper/Stepper.stories.tsx +245 -0
  41. package/src/components/custom/stepper/Stepper.test.tsx +230 -0
  42. package/src/components/custom/stepper/Stepper.tsx +250 -0
  43. package/src/components/custom/stepper/index.ts +10 -0
  44. package/src/components/index.ts +5 -0
  45. package/src/components/ui/alert/Alert.stories.tsx +154 -0
  46. package/src/components/ui/alert/Alert.test.tsx +202 -0
  47. package/src/components/ui/alert/Alert.tsx +170 -0
  48. package/src/components/ui/alert/index.ts +8 -0
  49. package/src/components/ui/autocomplete/Autocomplete.stories.tsx +299 -0
  50. package/src/components/ui/autocomplete/Autocomplete.test.tsx +247 -0
  51. package/src/components/ui/autocomplete/Autocomplete.tsx +332 -0
  52. package/src/components/ui/autocomplete/index.ts +2 -0
  53. package/src/components/ui/avatar/Avatar.stories.tsx +105 -0
  54. package/src/components/ui/avatar/Avatar.test.tsx +135 -0
  55. package/src/components/ui/avatar/Avatar.tsx +168 -0
  56. package/src/components/ui/avatar/index.ts +2 -0
  57. package/src/components/ui/badge/Badge.stories.tsx +100 -0
  58. package/src/components/ui/badge/Badge.test.tsx +86 -0
  59. package/src/components/ui/badge/Badge.tsx +105 -0
  60. package/src/components/ui/badge/index.ts +2 -0
  61. package/src/components/ui/breadcrumbs/Breadcrumbs.stories.tsx +70 -0
  62. package/src/components/ui/breadcrumbs/Breadcrumbs.test.tsx +156 -0
  63. package/src/components/ui/breadcrumbs/Breadcrumbs.tsx +127 -0
  64. package/src/components/ui/breadcrumbs/index.ts +2 -0
  65. package/src/components/ui/button/Button.stories.tsx +216 -0
  66. package/src/components/ui/button/Button.test.tsx +132 -0
  67. package/src/components/ui/button/Button.tsx +242 -0
  68. package/src/components/ui/button/index.ts +10 -0
  69. package/src/components/ui/card/Card.stories.tsx +443 -0
  70. package/src/components/ui/card/Card.test.tsx +265 -0
  71. package/src/components/ui/card/Card.tsx +380 -0
  72. package/src/components/ui/card/index.ts +20 -0
  73. package/src/components/ui/checkbox/Checkbox.stories.tsx +126 -0
  74. package/src/components/ui/checkbox/Checkbox.test.tsx +152 -0
  75. package/src/components/ui/checkbox/Checkbox.tsx +163 -0
  76. package/src/components/ui/checkbox/index.ts +2 -0
  77. package/src/components/ui/chip/Chip.stories.tsx +112 -0
  78. package/src/components/ui/chip/Chip.test.tsx +119 -0
  79. package/src/components/ui/chip/Chip.tsx +151 -0
  80. package/src/components/ui/chip/index.ts +2 -0
  81. package/src/components/ui/collapse/Collapse.stories.tsx +313 -0
  82. package/src/components/ui/collapse/Collapse.test.tsx +298 -0
  83. package/src/components/ui/collapse/Collapse.tsx +275 -0
  84. package/src/components/ui/collapse/index.ts +18 -0
  85. package/src/components/ui/data-row/DataRow.stories.tsx +78 -0
  86. package/src/components/ui/data-row/DataRow.test.tsx +78 -0
  87. package/src/components/ui/data-row/DataRow.tsx +29 -0
  88. package/src/components/ui/data-row/index.ts +2 -0
  89. package/src/components/ui/divider/Divider.stories.tsx +71 -0
  90. package/src/components/ui/divider/Divider.test.tsx +55 -0
  91. package/src/components/ui/divider/Divider.tsx +37 -0
  92. package/src/components/ui/divider/index.ts +2 -0
  93. package/src/components/ui/drawer/Drawer.stories.tsx +228 -0
  94. package/src/components/ui/drawer/Drawer.test.tsx +142 -0
  95. package/src/components/ui/drawer/Drawer.tsx +228 -0
  96. package/src/components/ui/drawer/index.ts +9 -0
  97. package/src/components/ui/form-field/FormField.stories.tsx +260 -0
  98. package/src/components/ui/form-field/FormField.test.tsx +280 -0
  99. package/src/components/ui/form-field/FormField.tsx +308 -0
  100. package/src/components/ui/form-field/index.ts +2 -0
  101. package/src/components/ui/help-tip/HelpTip.stories.tsx +250 -0
  102. package/src/components/ui/help-tip/HelpTip.test.tsx +198 -0
  103. package/src/components/ui/help-tip/HelpTip.tsx +229 -0
  104. package/src/components/ui/help-tip/index.ts +2 -0
  105. package/src/components/ui/icon-box/IconBox.stories.tsx +139 -0
  106. package/src/components/ui/icon-box/IconBox.test.tsx +120 -0
  107. package/src/components/ui/icon-box/IconBox.tsx +57 -0
  108. package/src/components/ui/icon-box/index.ts +1 -0
  109. package/src/components/ui/index.ts +36 -0
  110. package/src/components/ui/input/Input.stories.tsx +192 -0
  111. package/src/components/ui/input/Input.test.tsx +86 -0
  112. package/src/components/ui/input/Input.tsx +273 -0
  113. package/src/components/ui/input/index.ts +2 -0
  114. package/src/components/ui/link/Link.stories.tsx +78 -0
  115. package/src/components/ui/link/Link.test.tsx +94 -0
  116. package/src/components/ui/link/Link.tsx +86 -0
  117. package/src/components/ui/link/index.ts +2 -0
  118. package/src/components/ui/list-item/ListItem.stories.tsx +146 -0
  119. package/src/components/ui/list-item/ListItem.test.tsx +128 -0
  120. package/src/components/ui/list-item/ListItem.tsx +89 -0
  121. package/src/components/ui/list-item/index.ts +14 -0
  122. package/src/components/ui/menu/Menu.stories.tsx +180 -0
  123. package/src/components/ui/menu/Menu.test.tsx +353 -0
  124. package/src/components/ui/menu/Menu.tsx +222 -0
  125. package/src/components/ui/menu/index.ts +2 -0
  126. package/src/components/ui/modal/Modal.stories.tsx +350 -0
  127. package/src/components/ui/modal/Modal.test.tsx +159 -0
  128. package/src/components/ui/modal/Modal.tsx +254 -0
  129. package/src/components/ui/modal/index.ts +19 -0
  130. package/src/components/ui/popover/Popover.stories.tsx +190 -0
  131. package/src/components/ui/popover/Popover.test.tsx +277 -0
  132. package/src/components/ui/popover/Popover.tsx +162 -0
  133. package/src/components/ui/popover/index.ts +2 -0
  134. package/src/components/ui/progress/Progress.stories.tsx +190 -0
  135. package/src/components/ui/progress/Progress.test.tsx +181 -0
  136. package/src/components/ui/progress/Progress.tsx +320 -0
  137. package/src/components/ui/progress/index.ts +9 -0
  138. package/src/components/ui/radio/Radio.stories.tsx +199 -0
  139. package/src/components/ui/radio/Radio.test.tsx +166 -0
  140. package/src/components/ui/radio/Radio.tsx +200 -0
  141. package/src/components/ui/radio/index.ts +2 -0
  142. package/src/components/ui/section/Section.stories.tsx +111 -0
  143. package/src/components/ui/section/Section.test.tsx +121 -0
  144. package/src/components/ui/section/Section.tsx +52 -0
  145. package/src/components/ui/section/index.ts +6 -0
  146. package/src/components/ui/select/Select.stories.tsx +286 -0
  147. package/src/components/ui/select/Select.test.tsx +180 -0
  148. package/src/components/ui/select/Select.tsx +236 -0
  149. package/src/components/ui/select/index.ts +2 -0
  150. package/src/components/ui/skeleton/Skeleton.stories.tsx +153 -0
  151. package/src/components/ui/skeleton/Skeleton.test.tsx +162 -0
  152. package/src/components/ui/skeleton/Skeleton.tsx +229 -0
  153. package/src/components/ui/skeleton/index.ts +16 -0
  154. package/src/components/ui/spinner/Spinner.stories.tsx +67 -0
  155. package/src/components/ui/spinner/Spinner.test.tsx +67 -0
  156. package/src/components/ui/spinner/Spinner.tsx +72 -0
  157. package/src/components/ui/spinner/index.ts +2 -0
  158. package/src/components/ui/stack/Stack.stories.tsx +127 -0
  159. package/src/components/ui/stack/Stack.test.tsx +184 -0
  160. package/src/components/ui/stack/Stack.tsx +76 -0
  161. package/src/components/ui/stack/index.ts +2 -0
  162. package/src/components/ui/surface/Surface.stories.tsx +91 -0
  163. package/src/components/ui/surface/Surface.test.tsx +63 -0
  164. package/src/components/ui/surface/Surface.tsx +51 -0
  165. package/src/components/ui/surface/index.ts +1 -0
  166. package/src/components/ui/switch/Switch.stories.tsx +111 -0
  167. package/src/components/ui/switch/Switch.test.tsx +112 -0
  168. package/src/components/ui/switch/Switch.tsx +103 -0
  169. package/src/components/ui/switch/index.ts +2 -0
  170. package/src/components/ui/tabs/Tabs.stories.tsx +213 -0
  171. package/src/components/ui/tabs/Tabs.test.tsx +192 -0
  172. package/src/components/ui/tabs/Tabs.tsx +253 -0
  173. package/src/components/ui/tabs/index.ts +16 -0
  174. package/src/components/ui/toast/Toast.stories.tsx +255 -0
  175. package/src/components/ui/toast/Toast.test.tsx +207 -0
  176. package/src/components/ui/toast/Toast.tsx +327 -0
  177. package/src/components/ui/toast/index.ts +8 -0
  178. package/src/components/ui/toolbar-button/ToolbarButton.stories.tsx +415 -0
  179. package/src/components/ui/toolbar-button/ToolbarButton.test.tsx +224 -0
  180. package/src/components/ui/toolbar-button/ToolbarButton.tsx +307 -0
  181. package/src/components/ui/toolbar-button/index.ts +7 -0
  182. package/src/components/ui/tooltip/Tooltip.stories.tsx +126 -0
  183. package/src/components/ui/tooltip/Tooltip.test.tsx +179 -0
  184. package/src/components/ui/tooltip/Tooltip.tsx +143 -0
  185. package/src/components/ui/tooltip/index.ts +2 -0
  186. package/src/index.ts +10 -0
  187. package/src/test/setup.ts +19 -0
  188. package/src/theme/Colors.stories.tsx +258 -0
  189. package/src/theme/config.ts +193 -0
  190. package/src/theme/elevation.stories.tsx +346 -0
  191. package/src/theme/elevation.test.ts +27 -0
  192. package/src/theme/elevation.ts +567 -0
  193. package/src/theme/index.ts +39 -0
  194. package/src/theme/shadows.stories.tsx +523 -0
  195. package/src/theme/shadows.ts +439 -0
  196. package/src/theme/tokens/index.ts +2 -0
  197. package/src/theme/tokens/primitives.ts +354 -0
  198. package/src/theme/tokens/semantic.ts +381 -0
  199. package/src/types/jest-axe.d.ts +16 -0
  200. package/src/types/nativewind.d.ts +54 -0
  201. package/src/utils/cn.ts +14 -0
  202. package/src/utils/colors.ts +140 -0
  203. package/src/utils/form.ts +188 -0
  204. package/src/utils/index.ts +29 -0
  205. package/src/utils/useTheme.ts +72 -0
  206. package/dist/chunk-NA3EES23.mjs.map +0 -1
@@ -0,0 +1,381 @@
1
+ /**
2
+ * Semantic Design Tokens
3
+ *
4
+ * Meaningful tokens following DTCG (Design Tokens Community Group) naming conventions.
5
+ * Pattern: {category}-{intent}-{variant?}-{state?}
6
+ *
7
+ * Categories:
8
+ * - brand-* : Brand colors (primary, secondary, accent)
9
+ * - status-* : Feedback colors (success, error, warning, info)
10
+ * - result-* : Outcome indicators (improve, degrade, inconclusive, neutral)
11
+ * - data-* : Data visualization colors (chart series)
12
+ * - on-* : Text on colored backgrounds
13
+ * - surface-* : Elevated containers (NOT "paper")
14
+ * - background-* : Page backgrounds
15
+ * - text-* : Text colors
16
+ * - border-* : Border colors
17
+ * - interactive-* : Hover/focus/active/disabled states
18
+ */
19
+
20
+ import { primitiveColors as p, discreteRainbow } from './primitives'
21
+
22
+ // Light mode semantic colors (default)
23
+ export const semanticColorsLight = {
24
+ // Brand colors (brand-*)
25
+ 'brand-primary': p.accent[400], // #FF7900
26
+ 'brand-primary-light': p.accent[200], // #FF9630
27
+ 'brand-primary-dark': p.accent[700], // #D0620C
28
+ 'brand-primary-subtle': 'rgba(255, 121, 0, 0.08)',
29
+ 'brand-primary-hover': p.accent[500],
30
+ 'brand-primary-active': p.accent[600],
31
+
32
+ 'brand-secondary': p.steel[500], // #406D87
33
+ 'brand-secondary-light': p.steel[300], // #678498
34
+ 'brand-secondary-dark': p.steel[700], // #32556D
35
+ 'brand-secondary-subtle': 'rgba(64, 109, 135, 0.08)',
36
+ 'brand-secondary-hover': p.steel[600],
37
+ 'brand-secondary-active': p.steel[700],
38
+
39
+ // Text on brand backgrounds (on-*)
40
+ 'on-brand-primary': p.white,
41
+ 'on-brand-secondary': p.white,
42
+
43
+ // Status colors (status-*)
44
+ 'status-success': p.teal[500], // #14B8A6
45
+ 'status-success-light': p.teal[400], // #43C6B7
46
+ 'status-success-dark': p.teal[700], // #0E8074
47
+ 'status-success-subtle': p.teal[50],
48
+
49
+ 'status-error': p.red[600], // #D14343
50
+ 'status-error-light': p.red[400], // #DA6868
51
+ 'status-error-dark': p.red[700], // #922E2E
52
+ 'status-error-subtle': p.red[50],
53
+
54
+ 'status-warning': p.amber[500], // #FFB020
55
+ 'status-warning-light': p.amber[400], // #FFBF4C
56
+ 'status-warning-dark': p.amber[600], // #B27B16
57
+ 'status-warning-subtle': p.amber[50],
58
+
59
+ 'status-info': p.sky[500], // #2196F3
60
+ 'status-info-light': p.sky[400], // #64B6F7
61
+ 'status-info-dark': p.sky[700], // #0B79D0
62
+ 'status-info-subtle': p.sky[50],
63
+
64
+ // Text on status backgrounds (on-status-*)
65
+ 'on-status-success': p.white,
66
+ 'on-status-error': p.white,
67
+ 'on-status-warning': p.white,
68
+ 'on-status-info': p.white,
69
+
70
+ // Result/outcome indicators (result-*)
71
+ 'result-improve': '#4caf50', // Green - positive outcome
72
+ 'result-improve-light': 'rgba(76, 175, 80, 0.12)',
73
+ 'result-improve-dark': '#248a24',
74
+ 'result-degrade': '#ef5350', // Red - negative outcome
75
+ 'result-degrade-light': 'rgba(239, 83, 80, 0.12)',
76
+ 'result-degrade-dark': '#b30000',
77
+ 'result-inconclusive': '#9E9A97', // Gray - no clear result
78
+ 'result-inconclusive-light': 'rgba(158, 154, 151, 0.12)',
79
+ 'result-neutral': p.neutral[500], // Neutral baseline
80
+
81
+ // Text on result backgrounds (on-result-*)
82
+ 'on-result-improve': p.white,
83
+ 'on-result-degrade': p.white,
84
+ 'on-result-inconclusive': p.white,
85
+
86
+ // Data visualization colors (data-*)
87
+ // First 10 colors from discrete rainbow optimized for charts
88
+ 'data-1': discreteRainbow[9], // #1965B0 - Blue
89
+ 'data-2': discreteRainbow[14], // #4EB265 - Green
90
+ 'data-3': discreteRainbow[17], // #F7F056 - Yellow
91
+ 'data-4': discreteRainbow[25], // #DC050C - Red
92
+ 'data-5': discreteRainbow[8], // #882E72 - Purple
93
+ 'data-6': discreteRainbow[20], // #F4A736 - Orange
94
+ 'data-7': discreteRainbow[13], // #7BAFDE - Light Blue
95
+ 'data-8': discreteRainbow[15], // #90C987 - Light Green
96
+ 'data-9': discreteRainbow[3], // #CAACCB - Lavender
97
+ 'data-10': discreteRainbow[22], // #EE8026 - Dark Orange
98
+
99
+ // Text colors (text-*)
100
+ 'text-primary': '#121828',
101
+ 'text-secondary': '#65748B',
102
+ 'text-tertiary': p.neutral[400],
103
+ 'text-disabled': 'rgba(55, 65, 81, 0.48)',
104
+ 'text-inverse': p.white,
105
+ 'text-link': p.blue[600],
106
+ 'text-link-hover': p.blue[700],
107
+
108
+ // Surface colors (surface-*) - for elevated containers
109
+ 'surface-base': p.white,
110
+ 'surface-elevated': p.neutral[50], // #FAFAFA - slightly off-white for elevated cards
111
+ 'surface-raised': p.neutral[100], // #F3F4F6 - light gray for raised cards
112
+ 'surface-overlay': p.white,
113
+ 'surface-input': p.neutral[50], // Input field background (filled variant)
114
+
115
+ // Background colors (background-*)
116
+ 'background-base': '#EBEBEB',
117
+ 'background-default': p.white,
118
+ 'background-subtle': p.neutral[50],
119
+
120
+ // Border colors (border-*)
121
+ 'border-default': '#E8E9EB',
122
+ 'border-subtle': p.neutral[100],
123
+ 'border-strong': p.neutral[300],
124
+ 'border-focus': p.blue[600],
125
+ 'border-input': p.neutral[300], // Input field border
126
+ 'border-input-hover': p.neutral[400], // Input field border on hover
127
+ 'border-input-focus': p.blue[600], // Input field border on focus
128
+ 'border-input-error': p.red[600], // Input field border on error
129
+
130
+ // Interactive states (interactive-*)
131
+ 'interactive-hover': 'rgba(55, 65, 81, 0.04)',
132
+ 'interactive-focus': 'rgba(55, 65, 81, 0.12)',
133
+ 'interactive-active': 'rgba(55, 65, 81, 0.16)',
134
+ 'interactive-selected': 'rgba(55, 65, 81, 0.08)',
135
+ 'interactive-disabled': 'rgba(55, 65, 81, 0.12)',
136
+ 'interactive-disabled-text': 'rgba(55, 65, 81, 0.26)',
137
+
138
+ // Divider
139
+ 'divider': '#E8E9EB',
140
+
141
+ // Avatar default
142
+ 'avatar-background': p.neutral[500],
143
+ 'avatar-text': p.white,
144
+ } as const
145
+
146
+ // Dark mode semantic colors
147
+ export const semanticColorsDark = {
148
+ // Brand colors stay the same in dark mode
149
+ 'brand-primary': p.accent[400],
150
+ 'brand-primary-light': p.accent[200],
151
+ 'brand-primary-dark': p.accent[700],
152
+ 'brand-primary-subtle': 'rgba(255, 121, 0, 0.12)',
153
+ 'brand-primary-hover': p.accent[300],
154
+ 'brand-primary-active': p.accent[200],
155
+
156
+ 'brand-secondary': p.steel[500],
157
+ 'brand-secondary-light': p.steel[300],
158
+ 'brand-secondary-dark': p.steel[700],
159
+ 'brand-secondary-subtle': 'rgba(64, 109, 135, 0.12)',
160
+ 'brand-secondary-hover': p.steel[400],
161
+ 'brand-secondary-active': p.steel[300],
162
+
163
+ // Text on brand backgrounds
164
+ 'on-brand-primary': p.white,
165
+ 'on-brand-secondary': p.white,
166
+
167
+ // Status colors
168
+ 'status-success': p.teal[500],
169
+ 'status-success-light': p.teal[400],
170
+ 'status-success-dark': p.teal[700],
171
+ 'status-success-subtle': 'rgba(20, 184, 166, 0.12)',
172
+
173
+ 'status-error': p.red[600],
174
+ 'status-error-light': p.red[400],
175
+ 'status-error-dark': p.red[700],
176
+ 'status-error-subtle': 'rgba(209, 67, 67, 0.12)',
177
+
178
+ 'status-warning': p.amber[500],
179
+ 'status-warning-light': p.amber[400],
180
+ 'status-warning-dark': p.amber[600],
181
+ 'status-warning-subtle': 'rgba(255, 176, 32, 0.12)',
182
+
183
+ 'status-info': p.sky[500],
184
+ 'status-info-light': p.sky[400],
185
+ 'status-info-dark': p.sky[700],
186
+ 'status-info-subtle': 'rgba(33, 150, 243, 0.12)',
187
+
188
+ // Text on status backgrounds
189
+ 'on-status-success': p.white,
190
+ 'on-status-error': p.white,
191
+ 'on-status-warning': p.white,
192
+ 'on-status-info': p.white,
193
+
194
+ // Result/outcome indicators (result-*)
195
+ 'result-improve': '#4caf50',
196
+ 'result-improve-light': 'rgba(76, 175, 80, 0.16)',
197
+ 'result-improve-dark': '#248a24',
198
+ 'result-degrade': '#ef5350',
199
+ 'result-degrade-light': 'rgba(239, 83, 80, 0.16)',
200
+ 'result-degrade-dark': '#b30000',
201
+ 'result-inconclusive': '#9E9A97',
202
+ 'result-inconclusive-light': 'rgba(158, 154, 151, 0.16)',
203
+ 'result-neutral': p.neutral[400],
204
+
205
+ // Text on result backgrounds
206
+ 'on-result-improve': p.white,
207
+ 'on-result-degrade': p.white,
208
+ 'on-result-inconclusive': p.white,
209
+
210
+ // Data visualization colors (same in dark mode for consistency)
211
+ 'data-1': discreteRainbow[9],
212
+ 'data-2': discreteRainbow[14],
213
+ 'data-3': discreteRainbow[17],
214
+ 'data-4': discreteRainbow[25],
215
+ 'data-5': discreteRainbow[8],
216
+ 'data-6': discreteRainbow[20],
217
+ 'data-7': discreteRainbow[13],
218
+ 'data-8': discreteRainbow[15],
219
+ 'data-9': discreteRainbow[3],
220
+ 'data-10': discreteRainbow[22],
221
+
222
+ // Text colors - inverted for dark mode
223
+ 'text-primary': p.neutral[100],
224
+ 'text-secondary': p.neutral[400],
225
+ 'text-tertiary': p.neutral[500],
226
+ 'text-disabled': 'rgba(255, 255, 255, 0.38)',
227
+ 'text-inverse': p.neutral[900],
228
+ 'text-link': '#828DF8',
229
+ 'text-link-hover': p.blue[400],
230
+
231
+ // Surface colors - dark backgrounds
232
+ 'surface-base': p.charcoal[700], // #161616 - main surface
233
+ 'surface-elevated': p.charcoal[600], // #191919 - elevated surface
234
+ 'surface-raised': p.charcoal[500], // #1C1C1C - raised surface
235
+ 'surface-overlay': p.charcoal[600], // #191919 - overlay surface
236
+ 'surface-input': p.charcoal[600], // #191919 - input surface
237
+
238
+ // Background colors
239
+ 'background-base': p.charcoal[900], // #101010 - darkest charcoal
240
+ 'background-default': p.charcoal[700], // #161616 - main background
241
+ 'background-subtle': p.charcoal[500], // #1C1C1C - subtle background
242
+
243
+ // Border colors
244
+ 'border-default': p.charcoal[400], // #1F1F1F - default border
245
+ 'border-subtle': p.charcoal[500], // #1C1C1C - subtle border
246
+ 'border-strong': p.charcoal[300], // #2C2C2C - strong border
247
+ 'border-focus': '#828DF8',
248
+ 'border-input': p.neutral[600],
249
+ 'border-input-hover': p.neutral[500],
250
+ 'border-input-focus': '#828DF8',
251
+ 'border-input-error': p.red[500],
252
+
253
+ // Interactive states
254
+ 'interactive-hover': 'rgba(255, 255, 255, 0.04)',
255
+ 'interactive-focus': 'rgba(255, 255, 255, 0.12)',
256
+ 'interactive-active': 'rgba(255, 255, 255, 0.16)',
257
+ 'interactive-selected': 'rgba(255, 255, 255, 0.08)',
258
+ 'interactive-disabled': 'rgba(255, 255, 255, 0.12)',
259
+ 'interactive-disabled-text': 'rgba(255, 255, 255, 0.26)',
260
+
261
+ // Divider
262
+ 'divider': p.charcoal[400], // #1F1F1F - divider color
263
+
264
+ // Avatar default
265
+ 'avatar-background': p.neutral[600],
266
+ 'avatar-text': p.white,
267
+ } as const
268
+
269
+ // Typography tokens matching existing app
270
+ export const semanticTypography = {
271
+ // Headings (Space Grotesk)
272
+ h1: {
273
+ fontFamily: 'heading',
274
+ fontSize: '3.5rem',
275
+ fontWeight: 700,
276
+ lineHeight: 1.375,
277
+ },
278
+ h2: {
279
+ fontFamily: 'heading',
280
+ fontSize: '3rem',
281
+ fontWeight: 700,
282
+ lineHeight: 1.375,
283
+ },
284
+ h3: {
285
+ fontFamily: 'heading',
286
+ fontSize: '2.25rem',
287
+ fontWeight: 700,
288
+ lineHeight: 1.375,
289
+ },
290
+ h4: {
291
+ fontFamily: 'heading',
292
+ fontSize: '2rem',
293
+ fontWeight: 700,
294
+ lineHeight: 1.375,
295
+ },
296
+ h5: {
297
+ fontFamily: 'heading',
298
+ fontSize: '1.5rem',
299
+ fontWeight: 600,
300
+ lineHeight: 1.375,
301
+ },
302
+ h6: {
303
+ fontFamily: 'heading',
304
+ fontSize: '1.125rem',
305
+ fontWeight: 600,
306
+ lineHeight: 1.375,
307
+ },
308
+
309
+ // Body text (Nunito Sans)
310
+ body1: {
311
+ fontFamily: 'body',
312
+ fontSize: '1rem',
313
+ fontWeight: 400,
314
+ lineHeight: 1.5,
315
+ },
316
+ body2: {
317
+ fontFamily: 'body',
318
+ fontSize: '0.875rem',
319
+ fontWeight: 400,
320
+ lineHeight: 1.57,
321
+ },
322
+ subtitle1: {
323
+ fontFamily: 'body',
324
+ fontSize: '1rem',
325
+ fontWeight: 500,
326
+ lineHeight: 1.75,
327
+ },
328
+ subtitle2: {
329
+ fontFamily: 'body',
330
+ fontSize: '0.875rem',
331
+ fontWeight: 500,
332
+ lineHeight: 1.57,
333
+ },
334
+ caption: {
335
+ fontFamily: 'body',
336
+ fontSize: '0.75rem',
337
+ fontWeight: 400,
338
+ lineHeight: 1.66,
339
+ },
340
+ overline: {
341
+ fontFamily: 'body',
342
+ fontSize: '0.75rem',
343
+ fontWeight: 600,
344
+ lineHeight: 2.5,
345
+ letterSpacing: '0.5px',
346
+ textTransform: 'uppercase' as const,
347
+ },
348
+ button: {
349
+ fontFamily: 'sans',
350
+ fontSize: '0.875rem',
351
+ fontWeight: 600,
352
+ lineHeight: 1.75,
353
+ },
354
+ } as const
355
+
356
+ // Button size tokens
357
+ export const buttonSizes = {
358
+ sm: {
359
+ paddingX: '16px',
360
+ paddingY: '6px',
361
+ fontSize: '0.875rem',
362
+ },
363
+ md: {
364
+ paddingX: '20px',
365
+ paddingY: '8px',
366
+ fontSize: '0.875rem',
367
+ },
368
+ lg: {
369
+ paddingX: '24px',
370
+ paddingY: '11px',
371
+ fontSize: '1rem',
372
+ },
373
+ } as const
374
+
375
+ // Export type for theme mode
376
+ export type ThemeMode = 'light' | 'dark'
377
+
378
+ // Helper to get semantic colors based on mode
379
+ export function getSemanticColors(mode: ThemeMode) {
380
+ return mode === 'dark' ? semanticColorsDark : semanticColorsLight
381
+ }
@@ -0,0 +1,16 @@
1
+ declare module 'jest-axe' {
2
+ export function axe(
3
+ html: Element | string,
4
+ options?: Record<string, unknown>
5
+ ): Promise<{ violations: unknown[] }>
6
+
7
+ export const toHaveNoViolations: {
8
+ toHaveNoViolations(
9
+ results: { violations: unknown[] }
10
+ ): { pass: boolean; message(): string }
11
+ }
12
+
13
+ export function configureAxe(
14
+ options?: Record<string, unknown>
15
+ ): typeof axe
16
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * NativeWind type augmentations
3
+ *
4
+ * Adds `className` prop support to React Native components.
5
+ * This allows Tailwind classes to be used with NativeWind.
6
+ */
7
+
8
+ import 'react-native'
9
+
10
+ declare module 'react-native' {
11
+ interface ViewProps {
12
+ className?: string
13
+ }
14
+
15
+ interface TextProps {
16
+ className?: string
17
+ }
18
+
19
+ interface ImageProps {
20
+ className?: string
21
+ }
22
+
23
+ interface ScrollViewProps {
24
+ className?: string
25
+ }
26
+
27
+ interface TextInputProps {
28
+ className?: string
29
+ }
30
+
31
+ interface TouchableOpacityProps {
32
+ className?: string
33
+ }
34
+
35
+ interface TouchableHighlightProps {
36
+ className?: string
37
+ }
38
+
39
+ interface PressableProps {
40
+ className?: string
41
+ }
42
+
43
+ interface ActivityIndicatorProps {
44
+ className?: string
45
+ }
46
+
47
+ interface FlatListProps<ItemT> {
48
+ className?: string
49
+ }
50
+
51
+ interface SectionListProps<ItemT, SectionT> {
52
+ className?: string
53
+ }
54
+ }
@@ -0,0 +1,14 @@
1
+ import { clsx, type ClassValue } from 'clsx'
2
+ import { twMerge } from 'tailwind-merge'
3
+
4
+ /**
5
+ * Utility function to merge Tailwind CSS classes with proper conflict resolution.
6
+ * Combines clsx for conditional classes with tailwind-merge for deduplication.
7
+ *
8
+ * @example
9
+ * cn('p-4 bg-red-500', isActive && 'bg-blue-500', className)
10
+ * // If isActive is true, returns 'p-4 bg-blue-500' (not 'p-4 bg-red-500 bg-blue-500')
11
+ */
12
+ export function cn(...inputs: ClassValue[]): string {
13
+ return twMerge(clsx(inputs))
14
+ }
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Color utility functions for the design system
3
+ *
4
+ * lighten/darken are re-exported from shadows.ts which uses HSV-based
5
+ * color math for more perceptually consistent results across hues.
6
+ */
7
+
8
+ export { lighten, darken } from '../theme/shadows'
9
+
10
+ export type StatusType = 'success' | 'error' | 'warning' | 'info'
11
+ export type ResultType = 'improve' | 'degrade' | 'inconclusive' | 'neutral'
12
+
13
+ /**
14
+ * Get the semantic color token for a status type.
15
+ *
16
+ * @param status - The status type
17
+ * @returns The CSS variable reference for the status color
18
+ *
19
+ * @example
20
+ * const color = getStatusColor('success') // 'var(--color-status-success)'
21
+ */
22
+ export function getStatusColor(status: StatusType): string {
23
+ return `var(--color-status-${status})`
24
+ }
25
+
26
+ /**
27
+ * Get the semantic color token for a result type.
28
+ *
29
+ * @param result - The result type
30
+ * @returns The CSS variable reference for the result color
31
+ *
32
+ * @example
33
+ * const color = getResultColor('improve') // 'var(--color-result-improve)'
34
+ */
35
+ export function getResultColor(result: ResultType): string {
36
+ return `var(--color-result-${result})`
37
+ }
38
+
39
+ /**
40
+ * Apply alpha/opacity to a color.
41
+ * Works with hex colors and rgb values.
42
+ *
43
+ * @param color - The color value (hex or rgb)
44
+ * @param opacity - Opacity value between 0 and 1
45
+ * @returns The color with alpha applied
46
+ *
47
+ * @example
48
+ * alpha('#5048E5', 0.5) // 'rgba(80, 72, 229, 0.5)'
49
+ * alpha('rgb(80, 72, 229)', 0.5) // 'rgba(80, 72, 229, 0.5)'
50
+ */
51
+ export function alpha(color: string, opacity: number): string {
52
+ // Clamp opacity between 0 and 1
53
+ const clampedOpacity = Math.max(0, Math.min(1, opacity))
54
+
55
+ // Handle hex colors
56
+ if (color.startsWith('#')) {
57
+ const hex = color.slice(1)
58
+ let r: number, g: number, b: number
59
+
60
+ if (hex.length === 3) {
61
+ r = parseInt(hex[0] + hex[0], 16)
62
+ g = parseInt(hex[1] + hex[1], 16)
63
+ b = parseInt(hex[2] + hex[2], 16)
64
+ } else if (hex.length === 6) {
65
+ r = parseInt(hex.slice(0, 2), 16)
66
+ g = parseInt(hex.slice(2, 4), 16)
67
+ b = parseInt(hex.slice(4, 6), 16)
68
+ } else {
69
+ return color
70
+ }
71
+
72
+ return `rgba(${r}, ${g}, ${b}, ${clampedOpacity})`
73
+ }
74
+
75
+ // Handle rgb colors
76
+ const rgbMatch = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/)
77
+ if (rgbMatch) {
78
+ const [, r, g, b] = rgbMatch
79
+ return `rgba(${r}, ${g}, ${b}, ${clampedOpacity})`
80
+ }
81
+
82
+ // Handle rgba colors - replace the alpha value
83
+ const rgbaMatch = color.match(/rgba\((\d+),\s*(\d+),\s*(\d+),\s*[\d.]+\)/)
84
+ if (rgbaMatch) {
85
+ const [, r, g, b] = rgbaMatch
86
+ return `rgba(${r}, ${g}, ${b}, ${clampedOpacity})`
87
+ }
88
+
89
+ return color
90
+ }
91
+
92
+ /**
93
+ * Determine if a color is light or dark.
94
+ * Useful for determining text color contrast.
95
+ *
96
+ * @param color - Hex color value
97
+ * @returns 'light' or 'dark'
98
+ *
99
+ * @example
100
+ * getLuminance('#FFFFFF') // 'light'
101
+ * getLuminance('#000000') // 'dark'
102
+ */
103
+ export function getLuminance(color: string): 'light' | 'dark' {
104
+ if (!color.startsWith('#')) return 'dark'
105
+
106
+ const hex = color.slice(1)
107
+ let r: number, g: number, b: number
108
+
109
+ if (hex.length === 3) {
110
+ r = parseInt(hex[0] + hex[0], 16)
111
+ g = parseInt(hex[1] + hex[1], 16)
112
+ b = parseInt(hex[2] + hex[2], 16)
113
+ } else if (hex.length === 6) {
114
+ r = parseInt(hex.slice(0, 2), 16)
115
+ g = parseInt(hex.slice(2, 4), 16)
116
+ b = parseInt(hex.slice(4, 6), 16)
117
+ } else {
118
+ return 'dark'
119
+ }
120
+
121
+ // Calculate relative luminance using sRGB
122
+ const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255
123
+
124
+ return luminance > 0.5 ? 'light' : 'dark'
125
+ }
126
+
127
+ /**
128
+ * Get a contrasting text color for a given background.
129
+ *
130
+ * @param backgroundColor - The background color (hex)
131
+ * @returns 'white' or 'black' for contrast
132
+ *
133
+ * @example
134
+ * getContrastText('#5048E5') // 'white'
135
+ * getContrastText('#FFFFFF') // 'black'
136
+ */
137
+ export function getContrastText(backgroundColor: string): string {
138
+ return getLuminance(backgroundColor) === 'light' ? '#000000' : '#FFFFFF'
139
+ }
140
+