@altruist03/ui 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.
Files changed (207) hide show
  1. package/assets/images/noData.webp +0 -0
  2. package/dist/index.js +15237 -650
  3. package/dist/index.js.map +1 -1
  4. package/dist/noData-XPRHKMFR.webp +0 -0
  5. package/package.json +21 -7
  6. package/src/ThemeProvider/ThemeContext.tsx +75 -0
  7. package/src/ThemeProvider/index.ts +7 -0
  8. package/src/assets/images/noData.webp +0 -0
  9. package/src/assets.d.ts +10 -0
  10. package/src/components/Accordion/Accordion.stories.tsx +64 -0
  11. package/src/components/Accordion/Accordion.tsx +219 -0
  12. package/src/components/Accordion/Accordion.types.ts +16 -0
  13. package/src/components/Avatar/Avatar.stories.tsx +304 -0
  14. package/src/components/Avatar/Avatar.tsx +211 -0
  15. package/src/components/Avatar/Avatar.types.ts +33 -0
  16. package/src/components/Badge/Badge.stories.tsx +143 -0
  17. package/src/components/Badge/Badge.tsx +138 -0
  18. package/src/components/Badge/Badge.types.ts +24 -0
  19. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +114 -0
  20. package/src/components/Breadcrumb/Breadcrumb.tsx +120 -0
  21. package/src/components/Breadcrumb/Breadcrumb.types.ts +22 -0
  22. package/src/components/Button/Button.stories.tsx +457 -0
  23. package/src/components/Button/Button.tsx +202 -0
  24. package/src/components/Button/Button.types.ts +18 -0
  25. package/src/components/Checkbox/Checkbox.stories.tsx +254 -0
  26. package/src/components/Checkbox/Checkbox.tsx +352 -0
  27. package/src/components/Checkbox/Checkbox.types.ts +123 -0
  28. package/src/components/CloseButton/CloseButton.stories.tsx +100 -0
  29. package/src/components/CloseButton/CloseButton.tsx +117 -0
  30. package/src/components/CloseButton/CloseButton.types.ts +15 -0
  31. package/src/components/CollapsibleSearch/CollapsibleSearch.tsx +120 -0
  32. package/src/components/DataTable/DataTable.stories.tsx +233 -0
  33. package/src/components/DataTable/DataTable.tsx +968 -0
  34. package/src/components/DataTable/DataTable.types.ts +49 -0
  35. package/src/components/DatePicker/DatePicker.stories.tsx +341 -0
  36. package/src/components/DatePicker/DatePicker.tsx +669 -0
  37. package/src/components/DatePicker/DatePicker.types.ts +105 -0
  38. package/src/components/DatePicker/DateTimePicker.tsx +35 -0
  39. package/src/components/DateRangePicker/DateRangePicker.tsx +179 -0
  40. package/src/components/DateRangePicker/DateRangePicker.types.ts +34 -0
  41. package/src/components/Drawer/Drawer.stories.tsx +243 -0
  42. package/src/components/Drawer/Drawer.tsx +196 -0
  43. package/src/components/Drawer/Drawer.types.ts +60 -0
  44. package/src/components/FileUpload/FileUpload.stories.tsx +162 -0
  45. package/src/components/FileUpload/FileUpload.tsx +216 -0
  46. package/src/components/FileUpload/FileUpload.types.ts +80 -0
  47. package/src/components/FilterPopover/FilterPopover.stories.tsx +173 -0
  48. package/src/components/FilterPopover/FilterPopover.tsx +310 -0
  49. package/src/components/FilterPopover/FilterPopover.types.ts +26 -0
  50. package/src/components/ImageViewer/ImageViewerToolbar.jsx +81 -0
  51. package/src/components/ImageViewer/useImagePanRotate.js +102 -0
  52. package/src/components/IpWhitelistField/IpWhitelistField.stories.tsx +138 -0
  53. package/src/components/IpWhitelistField/IpWhitelistField.tsx +164 -0
  54. package/src/components/IpWhitelistField/IpWhitelistField.types.ts +42 -0
  55. package/src/components/Loader/Loader.stories.tsx +95 -0
  56. package/src/components/Loader/Loader.tsx +132 -0
  57. package/src/components/Loader/Loader.types.ts +6 -0
  58. package/src/components/Menu/Menu.tsx +143 -0
  59. package/src/components/Menu/Menu.types.ts +28 -0
  60. package/src/components/MenuButton/MenuButton.stories.tsx +140 -0
  61. package/src/components/MenuButton/MenuButton.tsx +45 -0
  62. package/src/components/MenuButton/MenuButton.types.ts +21 -0
  63. package/src/components/Modal/Modal.stories.tsx +266 -0
  64. package/src/components/Modal/Modal.tsx +399 -0
  65. package/src/components/Modal/index.ts +2 -0
  66. package/src/components/Modal/types.ts +126 -0
  67. package/src/components/Pagination/Pagination.stories.tsx +167 -0
  68. package/src/components/Pagination/Pagination.tsx +266 -0
  69. package/src/components/Pagination/Pagination.types.ts +20 -0
  70. package/src/components/PeriodSelector/PeriodSelector.stories.tsx +50 -0
  71. package/src/components/PeriodSelector/PeriodSelector.tsx +47 -0
  72. package/src/components/PeriodSelector/PeriodSelector.types.ts +10 -0
  73. package/src/components/PhoneField/PhoneField.stories.tsx +141 -0
  74. package/src/components/PhoneField/PhoneField.tsx +284 -0
  75. package/src/components/PhoneField/PhoneField.types.ts +89 -0
  76. package/src/components/PhoneField/countryCodes.ts +236 -0
  77. package/src/components/Progress/Progress.stories.tsx +83 -0
  78. package/src/components/Progress/Progress.tsx +91 -0
  79. package/src/components/Progress/Progress.types.ts +22 -0
  80. package/src/components/RadioButton/RadioButton.stories.tsx +332 -0
  81. package/src/components/RadioButton/RadioButton.tsx +427 -0
  82. package/src/components/RadioButton/RadioButton.types.ts +117 -0
  83. package/src/components/ReportCard/ReportCard.tsx +183 -0
  84. package/src/components/ReportCard/ReportCard.types.ts +17 -0
  85. package/src/components/SectionIcon/SectionIcon.tsx +87 -0
  86. package/src/components/SectionIcon/SectionIcon.types.ts +23 -0
  87. package/src/components/SegmentedInputField/SegmentedInputField.stories.tsx +165 -0
  88. package/src/components/SegmentedInputField/SegmentedInputField.tsx +174 -0
  89. package/src/components/SegmentedInputField/SegmentedInputField.types.ts +51 -0
  90. package/src/components/SelectScroll/SelectScroll.stories.tsx +336 -0
  91. package/src/components/SelectScroll/SelectScroll.tsx +1119 -0
  92. package/src/components/SelectScroll/SelectScroll.types.ts +60 -0
  93. package/src/components/Selector/Selector.stories.tsx +382 -0
  94. package/src/components/Selector/Selector.tsx +1310 -0
  95. package/src/components/Selector/Selector.types.ts +71 -0
  96. package/src/components/Skeleton/Skeleton.stories.tsx +133 -0
  97. package/src/components/Skeleton/Skeleton.tsx +20 -0
  98. package/src/components/Skeleton/Skeleton.types.ts +3 -0
  99. package/src/components/Spinner/Spinner.stories.tsx +84 -0
  100. package/src/components/Spinner/Spinner.tsx +79 -0
  101. package/src/components/Spinner/Spinner.types.ts +8 -0
  102. package/src/components/StatCard/StatCard.stories.tsx +125 -0
  103. package/src/components/StatCard/StatCard.tsx +167 -0
  104. package/src/components/StatCard/StatCard.types.ts +20 -0
  105. package/src/components/StatusBadge/StatusBadge.stories.tsx +179 -0
  106. package/src/components/StatusBadge/StatusBadge.tsx +74 -0
  107. package/src/components/StatusBadge/StatusBadge.types.ts +23 -0
  108. package/src/components/Stepper/Stepper.stories.tsx +85 -0
  109. package/src/components/Stepper/Stepper.tsx +138 -0
  110. package/src/components/Stepper/Stepper.types.ts +16 -0
  111. package/src/components/SubTabs/SubTabs.stories.tsx +128 -0
  112. package/src/components/SubTabs/SubTabs.tsx +279 -0
  113. package/src/components/SubTabs/SubTabs.types.ts +44 -0
  114. package/src/components/SummaryCard/SummaryCard.stories.tsx +100 -0
  115. package/src/components/SummaryCard/SummaryCard.tsx +141 -0
  116. package/src/components/SummaryCard/SummaryCard.types.ts +22 -0
  117. package/src/components/Switch/Switch.stories.tsx +59 -0
  118. package/src/components/Switch/Switch.tsx +217 -0
  119. package/src/components/Switch/Switch.types.ts +22 -0
  120. package/src/components/Tabs/Tabs.stories.tsx +178 -0
  121. package/src/components/Tabs/Tabs.tsx +197 -0
  122. package/src/components/Tabs/Tabs.types.ts +34 -0
  123. package/src/components/TextField/TextField.stories.tsx +249 -0
  124. package/src/components/TextField/TextField.tsx +462 -0
  125. package/src/components/TextField/TextField.types.ts +141 -0
  126. package/src/components/Toast/Toast.stories.tsx +237 -0
  127. package/src/components/Toast/Toast.tsx +246 -0
  128. package/src/components/Toast/Toast.types.ts +31 -0
  129. package/src/components/ToggleButton/ToggleButton.stories.tsx +105 -0
  130. package/src/components/ToggleButton/ToggleButton.tsx +267 -0
  131. package/src/components/ToggleButton/ToggleButton.types.ts +19 -0
  132. package/src/components/UploadDropzone/UploadDropzone.jsx +78 -0
  133. package/src/components/UploadDropzone2/UploadDropzone.stories.tsx +94 -0
  134. package/src/components/UploadDropzone2/UploadDropzone.tsx +222 -0
  135. package/src/components/UploadDropzone2/UploadDropzone.types.ts +54 -0
  136. package/src/components/ViewEditToggle/ViewEditToggle.stories.tsx +40 -0
  137. package/src/components/ViewEditToggle/ViewEditToggle.tsx +239 -0
  138. package/src/components/ViewEditToggle/ViewEditToggle.types.ts +27 -0
  139. package/src/components/index.ts +157 -0
  140. package/src/icons/ReportDollarIcon.tsx +71 -0
  141. package/src/icons/index.ts +27 -0
  142. package/src/index.ts +214 -0
  143. package/src/storybook/preview.tsx +64 -0
  144. package/src/theme/index.ts +4 -0
  145. package/src/theme/types.d.ts +1 -0
  146. package/src/themes/cpc.ts +4 -0
  147. package/src/themes/firebird.ts +4 -0
  148. package/src/tokens/brands/cpc/themes/dark.json +82 -0
  149. package/src/tokens/brands/cpc/themes/light.json +1 -0
  150. package/src/tokens/brands/default/brand.json +20 -0
  151. package/src/tokens/brands/firebird/themes/dark.json +28 -0
  152. package/src/tokens/brands/firebird/themes/light.json +20 -0
  153. package/src/tokens/button.ts +24 -0
  154. package/src/tokens/components/accordion.json +52 -0
  155. package/src/tokens/components/avatar.json +40 -0
  156. package/src/tokens/components/badge.json +41 -0
  157. package/src/tokens/components/breadcrumb.json +13 -0
  158. package/src/tokens/components/button.json +67 -0
  159. package/src/tokens/components/card.json +39 -0
  160. package/src/tokens/components/checkbox.json +64 -0
  161. package/src/tokens/components/closebutton.json +55 -0
  162. package/src/tokens/components/datatable.json +40 -0
  163. package/src/tokens/components/datepicker.json +99 -0
  164. package/src/tokens/components/drawer.json +23 -0
  165. package/src/tokens/components/field.json +29 -0
  166. package/src/tokens/components/fileupload.json +57 -0
  167. package/src/tokens/components/filterpopover.json +29 -0
  168. package/src/tokens/components/input.json +31 -0
  169. package/src/tokens/components/ipwhitelistfield.json +16 -0
  170. package/src/tokens/components/loader.json +15 -0
  171. package/src/tokens/components/menu.json +21 -0
  172. package/src/tokens/components/modal.json +25 -0
  173. package/src/tokens/components/pagination.json +45 -0
  174. package/src/tokens/components/phonefield.json +41 -0
  175. package/src/tokens/components/progress.json +12 -0
  176. package/src/tokens/components/radio.json +87 -0
  177. package/src/tokens/components/reportcard.json +17 -0
  178. package/src/tokens/components/selector.json +87 -0
  179. package/src/tokens/components/selectscroll.json +65 -0
  180. package/src/tokens/components/skeleton.json +6 -0
  181. package/src/tokens/components/stepper.json +38 -0
  182. package/src/tokens/components/subtabs.json +45 -0
  183. package/src/tokens/components/switch.json +55 -0
  184. package/src/tokens/components/tabs.json +38 -0
  185. package/src/tokens/components/textfield.json +59 -0
  186. package/src/tokens/components/togglebutton.json +48 -0
  187. package/src/tokens/components/uploaddropzone.json +61 -0
  188. package/src/tokens/drawer.ts +37 -0
  189. package/src/tokens/foundation/breakpoints.json +10 -0
  190. package/src/tokens/foundation/colors.json +122 -0
  191. package/src/tokens/foundation/opacity.json +16 -0
  192. package/src/tokens/foundation/radius.json +14 -0
  193. package/src/tokens/foundation/size.json +38 -0
  194. package/src/tokens/foundation/spacing.json +19 -0
  195. package/src/tokens/foundation/typography.json +43 -0
  196. package/src/tokens/index.ts +3665 -0
  197. package/src/tokens/modal.ts +42 -0
  198. package/src/tokens/semantic/color.json +148 -0
  199. package/src/tokens/semantic/field.json +28 -0
  200. package/src/tokens/semantic/opacity.json +5 -0
  201. package/src/tokens/semantic/size.json +722 -0
  202. package/src/tokens/semantic/spacing.json +27 -0
  203. package/src/tokens/semantic/typography.json +87 -0
  204. package/src/utils/scrollLock.ts +44 -0
  205. package/dist/index.css +0 -514
  206. package/dist/index.css.map +0 -1
  207. package/dist/index.d.ts +0 -289
@@ -0,0 +1,3665 @@
1
+ // Foundation tokens
2
+ import foundationSize from './foundation/size.json';
3
+ import foundationSpacing from './foundation/spacing.json';
4
+ import foundationRadius from './foundation/radius.json';
5
+ import foundationColors from './foundation/colors.json';
6
+ import foundationTypography from './foundation/typography.json';
7
+ import foundationOpacity from './foundation/opacity.json';
8
+ import foundationBreakpoints from './foundation/breakpoints.json';
9
+
10
+ // Semantic tokens
11
+ import semanticColor from './semantic/color.json';
12
+ import semanticSpacing from './semantic/spacing.json';
13
+ import semanticTypography from './semantic/typography.json';
14
+ import semanticField from './semantic/field.json';
15
+ import semanticSize from './semantic/size.json';
16
+ import semanticOpacity from './semantic/opacity.json';
17
+
18
+ // Component tokens
19
+ import componentButton from './components/button.json';
20
+ import componentInput from './components/input.json';
21
+ import componentTextfield from './components/textfield.json';
22
+ import componentCard from './components/card.json';
23
+ import componentCheckbox from './components/checkbox.json';
24
+ import componentSelectScroll from './components/selectscroll.json';
25
+ import componentSelector from './components/selector.json';
26
+ import componentDatePicker from './components/datepicker.json';
27
+ import componentField from './components/field.json';
28
+ import componentRadio from './components/radio.json';
29
+ import componentPagination from './components/pagination.json';
30
+ import componentCloseButton from './components/closebutton.json';
31
+ import componentAvatar from './components/avatar.json';
32
+ import componentTabs from './components/tabs.json';
33
+ import componentSubTabs from './components/subtabs.json';
34
+ import componentMenu from './components/menu.json';
35
+ import componentFileUpload from './components/fileupload.json';
36
+ import componentUploadDropzone from './components/uploaddropzone.json';
37
+ import componentPhonefield from './components/phonefield.json';
38
+ import componentBadge from './components/badge.json';
39
+ import componentFilterPopover from './components/filterpopover.json';
40
+ import componentToggleButton from './components/togglebutton.json';
41
+ import componentSwitch from './components/switch.json';
42
+ import componentBreadcrumb from './components/breadcrumb.json';
43
+ import componentLoader from './components/loader.json';
44
+ import componentDataTable from './components/datatable.json';
45
+ import componentModal from './components/modal.json';
46
+ import componentDrawer from './components/drawer.json';
47
+ import componentProgress from './components/progress.json';
48
+ import componentSkeleton from './components/skeleton.json';
49
+ import componentIpWhitelistField from './components/ipwhitelistfield.json';
50
+ import componentStepper from './components/stepper.json';
51
+ import componentAccordion from './components/accordion.json';
52
+ import componentReportCard from './components/reportcard.json';
53
+
54
+ // Brand theme tokens (per-brand, per-mode)
55
+ import firebirdLight from './brands/firebird/themes/light.json';
56
+ import firebirdDark from './brands/firebird/themes/dark.json';
57
+ import cpcLight from './brands/cpc/themes/light.json';
58
+ import cpcDark from './brands/cpc/themes/dark.json';
59
+
60
+ import type {
61
+ ButtonVariant,
62
+ ButtonSize,
63
+ ButtonShape,
64
+ ButtonColorToken,
65
+ ButtonSizeToken,
66
+ } from './button';
67
+
68
+ export type BrandKey = 'firebird' | 'cpc';
69
+ export type ThemeMode = 'light' | 'dark';
70
+
71
+ export type ScreenKey = 'mobile' | 'tablet' | 'laptop' | 'desktop' | 'wide' | 'ultra';
72
+
73
+ export interface GetTokensOptions {
74
+ brand?: BrandKey;
75
+ mode?: ThemeMode;
76
+ /** Current screen for responsive size resolution. If omitted, detected from window (client) or "desktop" (SSR). */
77
+ screen?: ScreenKey;
78
+ }
79
+
80
+ export interface ResolvedTokens {
81
+ foundation: Record<string, any>;
82
+ semantic: Record<string, any>;
83
+ components: Record<string, any>;
84
+ brands: Record<BrandKey, Record<ThemeMode, any>>;
85
+ }
86
+
87
+ /* ---------------------------------- */
88
+ /* Utilities */
89
+ /* ---------------------------------- */
90
+
91
+ function resolveTokenReferences(obj: any, tokens: any): any {
92
+ if (typeof obj === 'string') {
93
+ return obj.replace(/\{([^}]+)\}/g, (match, path) => {
94
+ const keys = path.split('.');
95
+ let value: any = tokens;
96
+ for (const key of keys) {
97
+ value = value?.[key];
98
+ if (value === undefined) break;
99
+ }
100
+ return value !== undefined ? value : match;
101
+ });
102
+ }
103
+
104
+ if (Array.isArray(obj)) {
105
+ return obj.map((item) => resolveTokenReferences(item, tokens));
106
+ }
107
+
108
+ if (obj && typeof obj === 'object') {
109
+ const resolved: any = {};
110
+ for (const key in obj) {
111
+ resolved[key] = resolveTokenReferences(obj[key], tokens);
112
+ }
113
+ return resolved;
114
+ }
115
+
116
+ return obj;
117
+ }
118
+
119
+ function deepMerge(base: any, override: any): any {
120
+ if (!override) return base;
121
+
122
+ const result = { ...base };
123
+
124
+ for (const key in override) {
125
+ const baseValue = base[key];
126
+ const overrideValue = override[key];
127
+
128
+ if (
129
+ baseValue &&
130
+ typeof baseValue === 'object' &&
131
+ !Array.isArray(baseValue) &&
132
+ overrideValue &&
133
+ typeof overrideValue === 'object' &&
134
+ !Array.isArray(overrideValue)
135
+ ) {
136
+ result[key] = deepMerge(baseValue, overrideValue);
137
+ } else {
138
+ result[key] = overrideValue;
139
+ }
140
+ }
141
+
142
+ return result;
143
+ }
144
+
145
+ export function getScreen(breakpoints: Record<string, number>): ScreenKey {
146
+ if (typeof window === 'undefined') return 'desktop';
147
+
148
+ const width = window.innerWidth;
149
+
150
+ if (width >= breakpoints.ultra) return 'ultra';
151
+ if (width >= breakpoints.wide) return 'wide';
152
+ if (width >= breakpoints.desktop) return 'desktop';
153
+ if (width >= breakpoints.laptop) return 'laptop';
154
+ if (width >= breakpoints.tablet) return 'tablet';
155
+ return 'mobile';
156
+ }
157
+
158
+ const DEFAULT_BREAKPOINTS: Record<string, number> = {
159
+ mobile: 0,
160
+ tablet: 768,
161
+ laptop: 1024,
162
+ desktop: 1440,
163
+ wide: 1920,
164
+ ultra: 2560,
165
+ };
166
+
167
+ /** Breakpoints used for responsive size resolution. Use with getScreen() and getTokens({ screen }). */
168
+ export function getBreakpoints(): Record<string, number> {
169
+ return (foundationBreakpoints as any)?.breakpoints ?
170
+ { ...(foundationBreakpoints as any).breakpoints }
171
+ : { ...DEFAULT_BREAKPOINTS };
172
+ }
173
+
174
+ const BREAKPOINT_KEYS = ['mobile', 'tablet', 'laptop', 'desktop', 'wide', 'ultra'];
175
+
176
+ function parsePxToNumber(value: unknown): number | undefined {
177
+ if (typeof value === 'number' && !Number.isNaN(value)) return value;
178
+ if (typeof value === 'string') {
179
+ const num = parseInt(value.replace(/px$/i, ''), 10);
180
+ return Number.isNaN(num) ? undefined : num;
181
+ }
182
+ return undefined;
183
+ }
184
+
185
+ function pickBreakpoint(obj: any, screen: ScreenKey): any {
186
+ const chosen = obj?.[screen];
187
+ if (chosen !== undefined) return chosen;
188
+ for (const bp of BREAKPOINT_KEYS) {
189
+ if (obj?.[bp] !== undefined) return obj[bp];
190
+ }
191
+ return undefined;
192
+ }
193
+
194
+ function resolveResponsiveSizes(rawSizes: any, screen: ScreenKey): any {
195
+ const resolved: any = {};
196
+
197
+ for (const sizeKey in rawSizes) {
198
+ const sizeToken = rawSizes[sizeKey];
199
+
200
+ // Non-responsive tokens (like control.radius)
201
+ if (!sizeToken.mobile && typeof sizeToken !== 'object') {
202
+ resolved[sizeKey] = sizeToken;
203
+ continue;
204
+ }
205
+
206
+ // Top-level breakpoint keys: pick one for current screen
207
+ const hasBreakpoint = BREAKPOINT_KEYS.some((bp) => sizeToken[bp]);
208
+ if (hasBreakpoint) {
209
+ resolved[sizeKey] = pickBreakpoint(sizeToken, screen);
210
+ continue;
211
+ }
212
+
213
+ // Nested responsive (e.g. radio: { sm: { mobile, tablet, ... }, md: {...}, lg: {...} })
214
+ const firstVal = sizeToken?.sm ?? sizeToken?.md ?? sizeToken?.lg;
215
+ const nestedHasBreakpoint =
216
+ firstVal && typeof firstVal === 'object' && BREAKPOINT_KEYS.some((bp) => firstVal[bp]);
217
+ if (nestedHasBreakpoint && sizeToken && typeof sizeToken === 'object') {
218
+ resolved[sizeKey] = {};
219
+ for (const variantKey of Object.keys(sizeToken)) {
220
+ const chosen = pickBreakpoint(sizeToken[variantKey], screen);
221
+ if (chosen !== undefined) resolved[sizeKey][variantKey] = chosen;
222
+ }
223
+ continue;
224
+ }
225
+
226
+ // Variant-only, no breakpoints — keep as-is
227
+ resolved[sizeKey] = sizeToken;
228
+ }
229
+
230
+ return resolved;
231
+ }
232
+
233
+ /* ---------------------------------- */
234
+ /* Foundation tokens */
235
+ /* ---------------------------------- */
236
+
237
+ const foundation = {
238
+ size: foundationSize.size,
239
+ spacing: foundationSpacing.spacing,
240
+ radius: foundationRadius.radius,
241
+ colors: foundationColors.colors,
242
+ typography: foundationTypography.typography,
243
+ opacity: foundationOpacity.opacity,
244
+ };
245
+
246
+ const allTokens = { ...foundation };
247
+
248
+ /* ---------------------------------- */
249
+ /* Semantic base */
250
+ /* ---------------------------------- */
251
+
252
+ const semanticBase = {
253
+ color: resolveTokenReferences(semanticColor.color, allTokens),
254
+ spacing: resolveTokenReferences(semanticSpacing.spacing, allTokens),
255
+ typography: resolveTokenReferences(semanticTypography.typography, allTokens),
256
+ opacity: resolveTokenReferences(semanticOpacity.opacity, allTokens),
257
+ };
258
+
259
+ /* ---------------------------------- */
260
+ /* Brand theme resolution (brand + mode) */
261
+ /* ---------------------------------- */
262
+
263
+ const resolvedBrandThemes = {
264
+ firebird: {
265
+ light: {
266
+ color: resolveTokenReferences(firebirdLight.color || {}, allTokens),
267
+ spacing: resolveTokenReferences((firebirdLight as any).spacing || {}, allTokens),
268
+ typography: resolveTokenReferences((firebirdLight as any).typography || {}, allTokens),
269
+ },
270
+ dark: {
271
+ color: resolveTokenReferences(firebirdDark.color || {}, allTokens),
272
+ spacing: resolveTokenReferences((firebirdDark as any).spacing || {}, allTokens),
273
+ typography: resolveTokenReferences((firebirdDark as any).typography || {}, allTokens),
274
+ },
275
+ },
276
+ cpc: {
277
+ light: {
278
+ color: resolveTokenReferences((cpcLight as any).color || {}, allTokens),
279
+ spacing: resolveTokenReferences((cpcLight as any).spacing || {}, allTokens),
280
+ typography: resolveTokenReferences((cpcLight as any).typography || {}, allTokens),
281
+ },
282
+ dark: {
283
+ color: resolveTokenReferences(cpcDark.color || {}, allTokens),
284
+ spacing: resolveTokenReferences((cpcDark as any).spacing || {}, allTokens),
285
+ typography: resolveTokenReferences((cpcDark as any).typography || {}, allTokens),
286
+ },
287
+ },
288
+ };
289
+
290
+ /* ---------------------------------- */
291
+ /* Active brand */
292
+ /* ---------------------------------- */
293
+
294
+ export const activeBrandKey: BrandKey =
295
+ (typeof process !== 'undefined' &&
296
+ (process as any).env?.DESIGN_SYSTEM_BRAND === 'firebird' &&
297
+ 'firebird') ||
298
+ 'cpc';
299
+
300
+ /* ---------------------------------- */
301
+ /* Main resolver */
302
+ /* ---------------------------------- */
303
+
304
+ export function getTokens(options: GetTokensOptions = {}): ResolvedTokens {
305
+ const brand: BrandKey = options.brand ?? activeBrandKey;
306
+ const mode: ThemeMode = options.mode ?? 'light';
307
+
308
+ // Step 1: semantic base
309
+ let mergedSemanticColor = { ...semanticBase.color };
310
+ let mergedSemanticSpacing = { ...semanticBase.spacing };
311
+ let mergedSemanticTypography = { ...semanticBase.typography };
312
+
313
+ // Step 2: apply brand-specific theme (brand + mode)
314
+ const brandTheme = resolvedBrandThemes[brand]?.[mode] || {};
315
+ mergedSemanticColor = deepMerge(mergedSemanticColor, brandTheme.color || {});
316
+ mergedSemanticSpacing = deepMerge(mergedSemanticSpacing, brandTheme.spacing || {});
317
+ mergedSemanticTypography = deepMerge(mergedSemanticTypography, brandTheme.typography || {});
318
+
319
+ const mergedSemanticOpacity = semanticBase.opacity;
320
+
321
+ const mergedSemantic = {
322
+ color: mergedSemanticColor,
323
+ spacing: mergedSemanticSpacing,
324
+ typography: mergedSemanticTypography,
325
+ opacity: mergedSemanticOpacity,
326
+ };
327
+
328
+ /* ---------- resolve semantic size ---------- */
329
+
330
+ const semanticContext = {
331
+ ...allTokens,
332
+ ...mergedSemantic,
333
+ spacing: {
334
+ ...(allTokens.spacing || {}),
335
+ ...(mergedSemantic.spacing || {}),
336
+ },
337
+ };
338
+
339
+ // Resolve raw semantic size tokens
340
+ const rawSemanticSize = resolveTokenReferences(semanticSize.size, semanticContext);
341
+
342
+ // Get breakpoints
343
+ const breakpoints = (foundationBreakpoints as any)?.breakpoints || {
344
+ mobile: 0,
345
+ tablet: 768,
346
+ laptop: 1024,
347
+ desktop: 1440,
348
+ wide: 1920,
349
+ ultra: 2560,
350
+ };
351
+
352
+ // Use provided screen or detect from window (client-only; SSR falls back to "desktop")
353
+ const screen = options.screen ?? getScreen(breakpoints);
354
+
355
+ // Resolve responsive sizes
356
+ const resolvedSemanticSize = resolveResponsiveSizes(rawSemanticSize, screen);
357
+
358
+ const resolvedSemanticField = resolveTokenReferences(semanticField.field, {
359
+ ...semanticContext,
360
+ semantic: {
361
+ ...mergedSemantic,
362
+ size: resolvedSemanticSize,
363
+ },
364
+ });
365
+
366
+ const finalSemantic = {
367
+ ...mergedSemantic,
368
+ size: resolvedSemanticSize,
369
+ field: resolvedSemanticField,
370
+ };
371
+
372
+ /* ---------- resolve components ---------- */
373
+
374
+ const componentContext = {
375
+ ...allTokens,
376
+ ...finalSemantic,
377
+ typography: {
378
+ ...(allTokens.typography || {}),
379
+ ...(finalSemantic.typography || {}),
380
+ },
381
+ semantic: finalSemantic,
382
+ spacing: {
383
+ ...(allTokens.spacing || {}),
384
+ ...(finalSemantic.spacing || {}),
385
+ },
386
+ size: {
387
+ ...(allTokens.size || {}),
388
+ ...(finalSemantic.size || {}),
389
+ },
390
+ };
391
+
392
+ const resolvedComponents = {
393
+ button: resolveTokenReferences(componentButton.button, componentContext),
394
+ input: resolveTokenReferences(componentInput.input, componentContext),
395
+ textfield: resolveTokenReferences(componentTextfield.textfield, componentContext),
396
+ card: resolveTokenReferences(componentCard.card, componentContext),
397
+ statCard: resolveTokenReferences(componentCard.statCard, componentContext),
398
+ checkbox: resolveTokenReferences(componentCheckbox.checkbox, componentContext),
399
+ selectscroll: resolveTokenReferences(componentSelectScroll.selectscroll, componentContext),
400
+ selector: resolveTokenReferences(componentSelector.selector, componentContext),
401
+ datepicker: resolveTokenReferences(componentDatePicker.datepicker, componentContext),
402
+ field: resolveTokenReferences(componentField.field, componentContext),
403
+ radio: resolveTokenReferences(componentRadio.radio, componentContext),
404
+ pagination: resolveTokenReferences(componentPagination.pagination, componentContext),
405
+ closebutton: resolveTokenReferences(componentCloseButton.closebutton, componentContext),
406
+ avatar: resolveTokenReferences(componentAvatar.avatar, componentContext),
407
+ tabs: resolveTokenReferences(componentTabs.tabs, componentContext),
408
+ subTabs: resolveTokenReferences(componentSubTabs.subTabs, componentContext),
409
+ menu: resolveTokenReferences(componentMenu.menu, componentContext),
410
+ fileupload: resolveTokenReferences(componentFileUpload.fileupload, componentContext),
411
+ uploaddropzone: resolveTokenReferences(
412
+ componentUploadDropzone.uploaddropzone,
413
+ componentContext
414
+ ),
415
+ phonefield: resolveTokenReferences(componentPhonefield.phonefield, componentContext),
416
+ badge: resolveTokenReferences(componentBadge.badge, componentContext),
417
+ filterpopover: resolveTokenReferences(componentFilterPopover.filterpopover, componentContext),
418
+ togglebutton: resolveTokenReferences(componentToggleButton.toggleButton, componentContext),
419
+ switch: resolveTokenReferences(componentSwitch.switch, componentContext),
420
+ breadcrumb: resolveTokenReferences(componentBreadcrumb.breadcrumb, componentContext),
421
+ loader: resolveTokenReferences(componentLoader.loader, componentContext),
422
+ datatable: resolveTokenReferences(componentDataTable.datatable, componentContext),
423
+ modal: resolveTokenReferences(componentModal.modal, componentContext),
424
+ drawer: resolveTokenReferences(componentDrawer.drawer, componentContext),
425
+ progress: resolveTokenReferences(componentProgress.progress, componentContext),
426
+ skeleton: resolveTokenReferences(componentSkeleton.skeleton, componentContext),
427
+ ipwhitelistfield: resolveTokenReferences(
428
+ componentIpWhitelistField.ipwhitelistfield,
429
+ componentContext
430
+ ),
431
+ stepper: resolveTokenReferences(componentStepper.stepper, componentContext),
432
+ accordion: resolveTokenReferences(componentAccordion.accordion, componentContext),
433
+ reportcard: resolveTokenReferences(componentReportCard.reportcard, componentContext),
434
+ };
435
+
436
+ return {
437
+ foundation,
438
+ semantic: finalSemantic,
439
+ components: resolvedComponents,
440
+ brands: resolvedBrandThemes,
441
+ };
442
+ }
443
+
444
+ /* ---------------------------------- */
445
+ /* Default export tokens */
446
+ /* ---------------------------------- */
447
+
448
+ const defaultResolved = getTokens({
449
+ brand: activeBrandKey,
450
+ mode: 'light',
451
+ });
452
+
453
+ const components = defaultResolved.components;
454
+ const mergedSemantic = defaultResolved.semantic;
455
+ const mergedSemanticColor = defaultResolved.semantic.color;
456
+
457
+ export const tokens = {
458
+ foundation: defaultResolved.foundation,
459
+ semantic: defaultResolved.semantic,
460
+ components: defaultResolved.components,
461
+ brands: defaultResolved.brands,
462
+ };
463
+
464
+ // Export individual token categories for convenience
465
+ export { foundation };
466
+ export { components };
467
+ export { mergedSemantic as semantic };
468
+ export { resolvedBrandThemes as brands };
469
+
470
+ // Legacy exports for backward compatibility
471
+ export const COLORS = {
472
+ primary: mergedSemanticColor.primary?.default ?? semanticBase.color.primary.default,
473
+ secondary: semanticBase.color.secondary.default,
474
+ ghostText: mergedSemanticColor.primary?.default ?? semanticBase.color.primary.default,
475
+ ghostHover: semanticBase.color.primary.light,
476
+ };
477
+
478
+ // Button token types (re-export from button.ts)
479
+ export type {
480
+ ButtonVariant,
481
+ ButtonSize,
482
+ ButtonShape,
483
+ ButtonColorToken,
484
+ ButtonSizeToken,
485
+ } from './button';
486
+
487
+ // Button tokens (resolved: component → semantic → brand → foundation; no hardcoded values)
488
+ export const BUTTON_COLORS: Record<ButtonVariant, ButtonColorToken> = {
489
+ blue: {
490
+ background: components.button.variant.blue.background,
491
+ color: components.button.variant.blue.color,
492
+ hover: components.button.variant.blue.hover.background,
493
+ border: components.button.variant.blue.border,
494
+ },
495
+ red: {
496
+ background: components.button.variant.red.background,
497
+ color: components.button.variant.red.color,
498
+ hover: components.button.variant.red.hover.background,
499
+ border: components.button.variant.red.border,
500
+ },
501
+ grey: {
502
+ background: components.button.variant.grey.background,
503
+ color: components.button.variant.grey.color,
504
+ hover: components.button.variant.grey.hover.background,
505
+ border: components.button.variant.grey.border,
506
+ },
507
+ };
508
+
509
+ export const BUTTON_SIZES: Record<ButtonSize, ButtonSizeToken> = {
510
+ sm: {
511
+ width: components.button.size.sm.width,
512
+ height: components.button.size.sm.height,
513
+ padding: components.button.size.sm.padding,
514
+ fontSize: parseInt(components.button.size.sm.fontSize),
515
+ borderRadius: components.button.size.sm.borderRadius,
516
+ contentGap: parseInt(String((components.button.size.sm as any)?.contentGap ?? '5'), 10),
517
+ },
518
+ md: {
519
+ width: components.button.size.md.width,
520
+ height: components.button.size.md.height,
521
+ padding: components.button.size.md.padding,
522
+ fontSize: parseInt(components.button.size.md.fontSize),
523
+ borderRadius: components.button.size.md.borderRadius,
524
+ contentGap: parseInt(String((components.button.size.md as any)?.contentGap ?? '8'), 10),
525
+ },
526
+ lg: {
527
+ width: components.button.size.lg.width,
528
+ height: components.button.size.lg.height,
529
+ padding: components.button.size.lg.padding,
530
+ fontSize: parseInt(components.button.size.lg.fontSize),
531
+ borderRadius: components.button.size.lg.borderRadius,
532
+ contentGap: parseInt(String((components.button.size.lg as any)?.contentGap ?? '12'), 10),
533
+ },
534
+ };
535
+
536
+ export const BUTTON_SHAPES: Record<ButtonShape, string | number> = {
537
+ rounded: components.button.shape.rounded,
538
+ square: components.button.shape.square,
539
+ pill: components.button.shape.pill,
540
+ };
541
+
542
+ export interface ButtonTokensFromResolved {
543
+ colors: Record<ButtonVariant, ButtonColorToken>;
544
+ sizes: Record<ButtonSize, ButtonSizeToken>;
545
+ }
546
+
547
+ /** Build Button tokens from resolved tokens (e.g. from useTokens()). Use this so tokens update when theme mode (light/dark) or brand changes. */
548
+ export function getButtonTokensFromResolved(resolved: ResolvedTokens): ButtonTokensFromResolved {
549
+ const btn = resolved.components?.button as Record<string, any> | undefined;
550
+ if (!btn?.variant || !btn?.size) {
551
+ return { colors: BUTTON_COLORS, sizes: BUTTON_SIZES };
552
+ }
553
+ const v = btn.variant;
554
+ const s = btn.size;
555
+ const blue = v.blue ?? {};
556
+ const red = v.red ?? {};
557
+ const grey = v.grey ?? {};
558
+ const sm = s.sm ?? {};
559
+ const md = s.md ?? {};
560
+ const lg = s.lg ?? {};
561
+ return {
562
+ colors: {
563
+ blue: {
564
+ background: blue.background ?? BUTTON_COLORS.blue.background,
565
+ color: blue.color ?? BUTTON_COLORS.blue.color,
566
+ hover: blue.hover?.background ?? BUTTON_COLORS.blue.hover,
567
+ hoverTextColor: blue.hover?.color,
568
+ border: blue.border ?? BUTTON_COLORS.blue.border,
569
+ },
570
+ red: {
571
+ background: red.background ?? BUTTON_COLORS.red.background,
572
+ color: red.color ?? BUTTON_COLORS.red.color,
573
+ hover: red.hover?.background ?? BUTTON_COLORS.red.hover,
574
+ hoverTextColor: red.hover?.color,
575
+ border: red.border ?? BUTTON_COLORS.red.border,
576
+ },
577
+ grey: {
578
+ background: grey.background ?? BUTTON_COLORS.grey.background,
579
+ color: grey.color ?? BUTTON_COLORS.grey.color,
580
+ hover: grey.hover?.background ?? BUTTON_COLORS.grey.hover,
581
+ hoverTextColor: grey.hover?.color,
582
+ border: grey.border ?? BUTTON_COLORS.grey.border,
583
+ },
584
+ },
585
+ sizes: {
586
+ sm: {
587
+ width: sm.width,
588
+ height: sm.height ?? BUTTON_SIZES.sm.height,
589
+ padding: sm.padding ?? BUTTON_SIZES.sm.padding,
590
+ fontSize:
591
+ typeof sm.fontSize === 'number' ?
592
+ sm.fontSize
593
+ : parseInt(String(sm.fontSize ?? BUTTON_SIZES.sm.fontSize), 10),
594
+ borderRadius: sm.borderRadius ?? BUTTON_SIZES.sm.borderRadius,
595
+ contentGap: parseInt(
596
+ String((sm as any)?.contentGap ?? BUTTON_SIZES.sm.contentGap ?? 5),
597
+ 10
598
+ ),
599
+ },
600
+ md: {
601
+ width: md.width,
602
+ height: md.height ?? BUTTON_SIZES.md.height,
603
+ padding: md.padding ?? BUTTON_SIZES.md.padding,
604
+ fontSize:
605
+ typeof md.fontSize === 'number' ?
606
+ md.fontSize
607
+ : parseInt(String(md.fontSize ?? BUTTON_SIZES.md.fontSize), 10),
608
+ borderRadius: md.borderRadius ?? BUTTON_SIZES.md.borderRadius,
609
+ contentGap: parseInt(
610
+ String((md as any)?.contentGap ?? BUTTON_SIZES.md.contentGap ?? 8),
611
+ 10
612
+ ),
613
+ },
614
+ lg: {
615
+ width: lg.width,
616
+ height: lg.height ?? BUTTON_SIZES.lg.height,
617
+ padding: lg.padding ?? BUTTON_SIZES.lg.padding,
618
+ fontSize:
619
+ typeof lg.fontSize === 'number' ?
620
+ lg.fontSize
621
+ : parseInt(String(lg.fontSize ?? BUTTON_SIZES.lg.fontSize), 10),
622
+ borderRadius: lg.borderRadius ?? BUTTON_SIZES.lg.borderRadius,
623
+ contentGap: parseInt(
624
+ String((lg as any)?.contentGap ?? BUTTON_SIZES.lg.contentGap ?? 12),
625
+ 10
626
+ ),
627
+ },
628
+ },
629
+ };
630
+ }
631
+
632
+ // CloseButton token types (aligned with components/closebutton.json)
633
+ export type CloseButtonVariant = 'filled' | 'outline';
634
+ export type CloseButtonSize = 'xs' | 'sm' | 'md' | 'lg';
635
+
636
+ // CloseButton tokens (legacy format; from components/closebutton.json). Flattened like TextFieldTokens.
637
+ export interface CloseButtonTokens {
638
+ sizeXs: string;
639
+ sizeSm: string;
640
+ sizeMd: string;
641
+ sizeLg: string;
642
+ paddingXs: string;
643
+ paddingSm: string;
644
+ paddingMd: string;
645
+ paddingLg: string;
646
+ iconSizeXs: string;
647
+ iconSizeSm: string;
648
+ iconSizeMd: string;
649
+ iconSizeLg: string;
650
+ borderRadiusXs: string;
651
+ borderRadiusSm: string;
652
+ borderRadiusMd: string;
653
+ borderRadiusLg: string;
654
+ filledBackground: string;
655
+ filledBackgroundWhite: string;
656
+ filledColor: string;
657
+ filledHoverBackground: string;
658
+ filledHoverBackgroundWhite: string;
659
+ filledHoverColor: string;
660
+ filledBorder: string;
661
+ outlineBackground: string;
662
+ outlineColor: string;
663
+ outlineBorder: string;
664
+ outlineHoverBorder: string;
665
+ outlineHoverColor: string;
666
+ disabledOpacity: string;
667
+ }
668
+
669
+ const closeBtn = components.closebutton;
670
+ const closeBtnSize = closeBtn.size;
671
+ export const CLOSEBUTTON_TOKENS: CloseButtonTokens = {
672
+ sizeXs: closeBtnSize.xs.size,
673
+ sizeSm: closeBtnSize.sm.size,
674
+ sizeMd: closeBtnSize.md.size,
675
+ sizeLg: closeBtnSize.lg.size,
676
+ paddingXs: closeBtnSize.xs.padding,
677
+ paddingSm: closeBtnSize.sm.padding,
678
+ paddingMd: closeBtnSize.md.padding,
679
+ paddingLg: closeBtnSize.lg.padding,
680
+ iconSizeXs: closeBtnSize.xs.iconSize,
681
+ iconSizeSm: closeBtnSize.sm.iconSize,
682
+ iconSizeMd: closeBtnSize.md.iconSize,
683
+ iconSizeLg: closeBtnSize.lg.iconSize,
684
+ borderRadiusXs: closeBtnSize.xs.borderRadius,
685
+ borderRadiusSm: closeBtnSize.sm.borderRadius,
686
+ borderRadiusMd: closeBtnSize.md.borderRadius,
687
+ borderRadiusLg: closeBtnSize.lg.borderRadius,
688
+ filledBackground: closeBtn.variant.filled.background,
689
+ filledBackgroundWhite:
690
+ closeBtn.variant.filled.backgroundWhite ?? closeBtn.variant.filled.background,
691
+ filledColor: closeBtn.variant.filled.color,
692
+ filledHoverBackground:
693
+ closeBtn.variant.filled.hover?.background ?? closeBtn.variant.filled.background,
694
+ filledHoverBackgroundWhite:
695
+ closeBtn.variant.filled.hover?.backgroundWhite ??
696
+ closeBtn.variant.filled.backgroundWhite ??
697
+ closeBtn.variant.filled.background,
698
+ filledHoverColor: closeBtn.variant.filled.hover?.color ?? closeBtn.variant.filled.color,
699
+ filledBorder: closeBtn.variant.filled.border,
700
+ outlineBackground: closeBtn.variant.outline.background,
701
+ outlineColor: closeBtn.variant.outline.color,
702
+ outlineBorder: closeBtn.variant.outline.border,
703
+ outlineHoverBorder: closeBtn.variant.outline.hover?.border ?? closeBtn.variant.outline.border,
704
+ outlineHoverColor: closeBtn.variant.outline.hover?.color ?? closeBtn.variant.outline.color,
705
+ disabledOpacity: closeBtn.disabled.opacity,
706
+ };
707
+
708
+ const defaultCloseButtonTokens = CLOSEBUTTON_TOKENS;
709
+
710
+ /** Build CloseButton tokens from resolved tokens (e.g. from useTokens()). Use this so tokens update when screen/brand/mode changes. */
711
+ export function getCloseButtonTokensFromResolved(resolved: ResolvedTokens): CloseButtonTokens {
712
+ const closeBtnResolved = resolved.components?.closebutton;
713
+ if (!closeBtnResolved?.size) return defaultCloseButtonTokens;
714
+ const xs = closeBtnResolved.size.xs as any;
715
+ const sm = closeBtnResolved.size.sm as any;
716
+ const md = closeBtnResolved.size.md as any;
717
+ const lg = closeBtnResolved.size.lg as any;
718
+ const filled = closeBtnResolved.variant?.filled;
719
+ const outline = closeBtnResolved.variant?.outline;
720
+ return {
721
+ sizeXs: xs?.size ?? defaultCloseButtonTokens.sizeXs,
722
+ sizeSm: sm?.size ?? defaultCloseButtonTokens.sizeSm,
723
+ sizeMd: md?.size ?? defaultCloseButtonTokens.sizeMd,
724
+ sizeLg: lg?.size ?? defaultCloseButtonTokens.sizeLg,
725
+ paddingXs: xs?.padding ?? defaultCloseButtonTokens.paddingXs,
726
+ paddingSm: sm?.padding ?? defaultCloseButtonTokens.paddingSm,
727
+ paddingMd: md?.padding ?? defaultCloseButtonTokens.paddingMd,
728
+ paddingLg: lg?.padding ?? defaultCloseButtonTokens.paddingLg,
729
+ iconSizeXs: xs?.iconSize ?? defaultCloseButtonTokens.iconSizeXs,
730
+ iconSizeSm: sm?.iconSize ?? defaultCloseButtonTokens.iconSizeSm,
731
+ iconSizeMd: md?.iconSize ?? defaultCloseButtonTokens.iconSizeMd,
732
+ iconSizeLg: lg?.iconSize ?? defaultCloseButtonTokens.iconSizeLg,
733
+ borderRadiusXs: xs?.borderRadius ?? defaultCloseButtonTokens.borderRadiusXs,
734
+ borderRadiusSm: sm?.borderRadius ?? defaultCloseButtonTokens.borderRadiusSm,
735
+ borderRadiusMd: md?.borderRadius ?? defaultCloseButtonTokens.borderRadiusMd,
736
+ borderRadiusLg: lg?.borderRadius ?? defaultCloseButtonTokens.borderRadiusLg,
737
+ filledBackground: filled?.background ?? defaultCloseButtonTokens.filledBackground,
738
+ filledBackgroundWhite:
739
+ filled?.backgroundWhite ?? defaultCloseButtonTokens.filledBackgroundWhite,
740
+ filledColor: filled?.color ?? defaultCloseButtonTokens.filledColor,
741
+ filledHoverBackground:
742
+ filled?.hover?.background ?? defaultCloseButtonTokens.filledHoverBackground,
743
+ filledHoverBackgroundWhite:
744
+ filled?.hover?.backgroundWhite ?? defaultCloseButtonTokens.filledHoverBackgroundWhite,
745
+ filledHoverColor: filled?.hover?.color ?? defaultCloseButtonTokens.filledHoverColor,
746
+ filledBorder: filled?.border ?? defaultCloseButtonTokens.filledBorder,
747
+ outlineBackground: outline?.background ?? defaultCloseButtonTokens.outlineBackground,
748
+ outlineColor: outline?.color ?? defaultCloseButtonTokens.outlineColor,
749
+ outlineBorder: outline?.border ?? defaultCloseButtonTokens.outlineBorder,
750
+ outlineHoverBorder: outline?.hover?.border ?? defaultCloseButtonTokens.outlineHoverBorder,
751
+ outlineHoverColor: outline?.hover?.color ?? defaultCloseButtonTokens.outlineHoverColor,
752
+ disabledOpacity: closeBtnResolved.disabled?.opacity ?? defaultCloseButtonTokens.disabledOpacity,
753
+ };
754
+ }
755
+
756
+ // Avatar token types (aligned with components/avatar.json)
757
+ export type AvatarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl' | 'xxxl';
758
+
759
+ export interface AvatarTokens {
760
+ background: string;
761
+ textColor: string;
762
+ iconColor: string;
763
+ placeholderBackground: string;
764
+ placeholderShape: string;
765
+ shapeCircle: string;
766
+ shapeSquare: string;
767
+ sizeXs: string;
768
+ sizeSm: string;
769
+ sizeMd: string;
770
+ sizeLg: string;
771
+ sizeXl: string;
772
+ sizeXxl: string;
773
+ sizeXxxl: string;
774
+ fontSizeXs: string;
775
+ fontSizeSm: string;
776
+ fontSizeMd: string;
777
+ fontSizeLg: string;
778
+ fontSizeXl: string;
779
+ fontSizeXxl: string;
780
+ fontSizeXxxl: string;
781
+ iconSizeXs: string;
782
+ iconSizeSm: string;
783
+ iconSizeMd: string;
784
+ iconSizeLg: string;
785
+ iconSizeXl: string;
786
+ iconSizeXxl: string;
787
+ iconSizeXxxl: string;
788
+ }
789
+
790
+ const avatarComp = components.avatar;
791
+ const avatarSize = avatarComp.size;
792
+ const avatarFontSize = avatarComp.fontSize;
793
+ const avatarIconSize = avatarComp.iconSize;
794
+ export const AVATAR_TOKENS: AvatarTokens = {
795
+ background: avatarComp.background,
796
+ textColor: avatarComp.textColor,
797
+ iconColor: avatarComp.iconColor,
798
+ placeholderBackground: avatarComp.placeholderBackground,
799
+ placeholderShape: avatarComp.placeholderShape,
800
+ shapeCircle: avatarComp.shape?.circle ?? '9999px',
801
+ shapeSquare: avatarComp.shape?.square ?? '2px',
802
+ sizeXs: avatarSize?.xs ?? '20px',
803
+ sizeSm: avatarSize?.sm ?? '28px',
804
+ sizeMd: avatarSize?.md ?? '36px',
805
+ sizeLg: avatarSize?.lg ?? '44px',
806
+ sizeXl: avatarSize?.xl ?? '60px',
807
+ sizeXxl: avatarSize?.xxl ?? '76px',
808
+ sizeXxxl: avatarSize?.xxxl ?? '116px',
809
+ fontSizeXs: avatarFontSize?.xs ?? '12px',
810
+ fontSizeSm: avatarFontSize?.sm ?? '14px',
811
+ fontSizeMd: avatarFontSize?.md ?? '16px',
812
+ fontSizeLg: avatarFontSize?.lg ?? '18px',
813
+ fontSizeXl: avatarFontSize?.xl ?? '20px',
814
+ fontSizeXxl: avatarFontSize?.xxl ?? '24px',
815
+ fontSizeXxxl: avatarFontSize?.xxxl ?? '30px',
816
+ iconSizeXs: avatarIconSize?.xs ?? '12px',
817
+ iconSizeSm: avatarIconSize?.sm ?? '14px',
818
+ iconSizeMd: avatarIconSize?.md ?? '16px',
819
+ iconSizeLg: avatarIconSize?.lg ?? '18px',
820
+ iconSizeXl: avatarIconSize?.xl ?? '20px',
821
+ iconSizeXxl: avatarIconSize?.xxl ?? '24px',
822
+ iconSizeXxxl: avatarIconSize?.xxxl ?? '30px',
823
+ };
824
+
825
+ const defaultAvatarTokens = AVATAR_TOKENS;
826
+
827
+ /** Build Avatar tokens from resolved tokens (e.g. from useTokens()). */
828
+ export function getAvatarTokensFromResolved(resolved: ResolvedTokens): AvatarTokens {
829
+ const a = resolved.components?.avatar;
830
+ if (!a) return defaultAvatarTokens;
831
+ const size = a.size ?? {};
832
+ const fontSize = a.fontSize ?? {};
833
+ const iconSize = a.iconSize ?? {};
834
+ return {
835
+ background: a.background ?? defaultAvatarTokens.background,
836
+ textColor: a.textColor ?? defaultAvatarTokens.textColor,
837
+ iconColor: a.iconColor ?? defaultAvatarTokens.iconColor,
838
+ placeholderBackground: a.placeholderBackground ?? defaultAvatarTokens.placeholderBackground,
839
+ placeholderShape: a.placeholderShape ?? defaultAvatarTokens.placeholderShape,
840
+ shapeCircle: a.shape?.circle ?? defaultAvatarTokens.shapeCircle,
841
+ shapeSquare: a.shape?.square ?? defaultAvatarTokens.shapeSquare,
842
+ sizeXs: size.xs ?? defaultAvatarTokens.sizeXs,
843
+ sizeSm: size.sm ?? defaultAvatarTokens.sizeSm,
844
+ sizeMd: size.md ?? defaultAvatarTokens.sizeMd,
845
+ sizeLg: size.lg ?? defaultAvatarTokens.sizeLg,
846
+ sizeXl: size.xl ?? defaultAvatarTokens.sizeXl,
847
+ sizeXxl: size.xxl ?? defaultAvatarTokens.sizeXxl,
848
+ sizeXxxl: size.xxxl ?? defaultAvatarTokens.sizeXxxl,
849
+ fontSizeXs: fontSize.xs ?? defaultAvatarTokens.fontSizeXs,
850
+ fontSizeSm: fontSize.sm ?? defaultAvatarTokens.fontSizeSm,
851
+ fontSizeMd: fontSize.md ?? defaultAvatarTokens.fontSizeMd,
852
+ fontSizeLg: fontSize.lg ?? defaultAvatarTokens.fontSizeLg,
853
+ fontSizeXl: fontSize.xl ?? defaultAvatarTokens.fontSizeXl,
854
+ fontSizeXxl: fontSize.xxl ?? defaultAvatarTokens.fontSizeXxl,
855
+ fontSizeXxxl: fontSize.xxxl ?? defaultAvatarTokens.fontSizeXxxl,
856
+ iconSizeXs: iconSize.xs ?? defaultAvatarTokens.iconSizeXs,
857
+ iconSizeSm: iconSize.sm ?? defaultAvatarTokens.iconSizeSm,
858
+ iconSizeMd: iconSize.md ?? defaultAvatarTokens.iconSizeMd,
859
+ iconSizeLg: iconSize.lg ?? defaultAvatarTokens.iconSizeLg,
860
+ iconSizeXl: iconSize.xl ?? defaultAvatarTokens.iconSizeXl,
861
+ iconSizeXxl: iconSize.xxl ?? defaultAvatarTokens.iconSizeXxl,
862
+ iconSizeXxxl: iconSize.xxxl ?? defaultAvatarTokens.iconSizeXxxl,
863
+ };
864
+ }
865
+
866
+ // Tabs token types (aligned with components/tabs.json)
867
+ // Figma node 52-10796: active tab = teal; inactive tab = white (#FFFFFF) background
868
+ export type TabsSize = 'sm' | 'md' | 'lg';
869
+
870
+ export interface TabsSizeToken {
871
+ height: string;
872
+ padding: string;
873
+ fontSize: number;
874
+ borderRadius: string;
875
+ fontWeight: string;
876
+ }
877
+
878
+ export interface TabsTokens {
879
+ activeBackground: string;
880
+ activeColor: string;
881
+ inactiveColor: string;
882
+ inactiveBackground: string;
883
+ inactiveBackgroundWhite: string;
884
+ containerBackground: string;
885
+ containerBackgroundWhite: string;
886
+ borderRadius: string;
887
+ containerBorderRadius: string;
888
+ paddingX: string;
889
+ paddingY: string;
890
+ gap: string;
891
+ fontSize: string;
892
+ fontWeight: string;
893
+ }
894
+
895
+ const tabsComp = components.tabs;
896
+ export const TABS_TOKENS: TabsTokens = {
897
+ activeBackground: tabsComp?.activeBackground ?? '#00545e',
898
+ activeColor: tabsComp?.activeColor ?? '#ffffff',
899
+ inactiveColor: tabsComp?.inactiveColor ?? '#6b7280',
900
+ inactiveBackground: tabsComp?.inactiveBackground ?? '#FFFFFF',
901
+ inactiveBackgroundWhite:
902
+ tabsComp?.inactiveBackgroundWhite ?? tabsComp?.inactiveBackground ?? '#FFFFFF',
903
+ containerBackground: tabsComp?.containerBackground ?? '#FFFFFF',
904
+ containerBackgroundWhite:
905
+ tabsComp?.containerBackgroundWhite ?? tabsComp?.containerBackground ?? '#FFFFFF',
906
+ borderRadius: tabsComp?.borderRadius ?? '12px',
907
+ containerBorderRadius: tabsComp?.containerBorderRadius ?? '12px',
908
+ paddingX: tabsComp?.paddingX ?? '12px',
909
+ paddingY: tabsComp?.paddingY ?? '6px',
910
+ gap: tabsComp?.gap ?? '8px',
911
+ fontSize: tabsComp?.fontSize ?? '14px',
912
+ fontWeight: tabsComp?.fontWeight ?? '500',
913
+ };
914
+
915
+ export const TABS_SIZES: Record<TabsSize, TabsSizeToken> = {
916
+ sm: {
917
+ height: tabsComp?.size?.sm?.height ?? '32px',
918
+ padding: tabsComp?.size?.sm?.padding ?? '6px 12px',
919
+ fontSize: parseInt(tabsComp?.size?.sm?.fontSize ?? '12'),
920
+ borderRadius: tabsComp?.size?.sm?.borderRadius ?? '12px',
921
+ fontWeight: tabsComp?.size?.sm?.fontWeight ?? '500',
922
+ },
923
+ md: {
924
+ height: tabsComp?.size?.md?.height ?? '40px',
925
+ padding: tabsComp?.size?.md?.padding ?? '8px 16px',
926
+ fontSize: parseInt(tabsComp?.size?.md?.fontSize ?? '14'),
927
+ borderRadius: tabsComp?.size?.md?.borderRadius ?? '12px',
928
+ fontWeight: tabsComp?.size?.md?.fontWeight ?? '500',
929
+ },
930
+ lg: {
931
+ height: tabsComp?.size?.lg?.height ?? '48px',
932
+ padding: tabsComp?.size?.lg?.padding ?? '12px 24px',
933
+ fontSize: parseInt(tabsComp?.size?.lg?.fontSize ?? '16'),
934
+ borderRadius: tabsComp?.size?.lg?.borderRadius ?? '12px',
935
+ fontWeight: tabsComp?.size?.lg?.fontWeight ?? '500',
936
+ },
937
+ };
938
+
939
+ export interface TabsTokensFromResolved {
940
+ tokens: TabsTokens;
941
+ sizes: Record<TabsSize, TabsSizeToken>;
942
+ }
943
+
944
+ const defaultTabsTokens = TABS_TOKENS;
945
+ const defaultTabsSizes = TABS_SIZES;
946
+
947
+ /** Build Tabs tokens from resolved tokens (e.g. from useTokens()). Use this so tokens update when theme mode (light/dark) or brand changes. */
948
+ export function getTabsTokensFromResolved(resolved: ResolvedTokens): TabsTokensFromResolved {
949
+ const t = resolved.components?.tabs as Record<string, any> | undefined;
950
+ if (!t) {
951
+ return { tokens: defaultTabsTokens, sizes: defaultTabsSizes };
952
+ }
953
+ const sm = t.size?.sm ?? {};
954
+ const md = t.size?.md ?? {};
955
+ const lg = t.size?.lg ?? {};
956
+ return {
957
+ tokens: {
958
+ activeBackground: t.activeBackground ?? defaultTabsTokens.activeBackground,
959
+ activeColor: t.activeColor ?? defaultTabsTokens.activeColor,
960
+ inactiveColor: t.inactiveColor ?? defaultTabsTokens.inactiveColor,
961
+ inactiveBackground: t.inactiveBackground ?? defaultTabsTokens.inactiveBackground,
962
+ inactiveBackgroundWhite:
963
+ t.inactiveBackgroundWhite ??
964
+ t.inactiveBackground ??
965
+ defaultTabsTokens.inactiveBackgroundWhite,
966
+ containerBackground: t.containerBackground ?? defaultTabsTokens.containerBackground,
967
+ containerBackgroundWhite:
968
+ t.containerBackgroundWhite ??
969
+ t.containerBackground ??
970
+ defaultTabsTokens.containerBackgroundWhite,
971
+ borderRadius: t.borderRadius ?? defaultTabsTokens.borderRadius,
972
+ containerBorderRadius: t.containerBorderRadius ?? defaultTabsTokens.containerBorderRadius,
973
+ paddingX: t.paddingX ?? defaultTabsTokens.paddingX,
974
+ paddingY: t.paddingY ?? defaultTabsTokens.paddingY,
975
+ gap: t.gap ?? defaultTabsTokens.gap,
976
+ fontSize: t.fontSize ?? defaultTabsTokens.fontSize,
977
+ fontWeight: t.fontWeight ?? defaultTabsTokens.fontWeight,
978
+ },
979
+ sizes: {
980
+ sm: {
981
+ height: sm.height ?? defaultTabsSizes.sm.height,
982
+ padding: sm.padding ?? defaultTabsSizes.sm.padding,
983
+ fontSize:
984
+ typeof sm.fontSize === 'number' ?
985
+ sm.fontSize
986
+ : parseInt(String(sm.fontSize ?? defaultTabsSizes.sm.fontSize), 10),
987
+ borderRadius: sm.borderRadius ?? defaultTabsSizes.sm.borderRadius,
988
+ fontWeight: sm.fontWeight ?? defaultTabsSizes.sm.fontWeight,
989
+ },
990
+ md: {
991
+ height: md.height ?? defaultTabsSizes.md.height,
992
+ padding: md.padding ?? defaultTabsSizes.md.padding,
993
+ fontSize:
994
+ typeof md.fontSize === 'number' ?
995
+ md.fontSize
996
+ : parseInt(String(md.fontSize ?? defaultTabsSizes.md.fontSize), 10),
997
+ borderRadius: md.borderRadius ?? defaultTabsSizes.md.borderRadius,
998
+ fontWeight: md.fontWeight ?? defaultTabsSizes.md.fontWeight,
999
+ },
1000
+ lg: {
1001
+ height: lg.height ?? defaultTabsSizes.lg.height,
1002
+ padding: lg.padding ?? defaultTabsSizes.lg.padding,
1003
+ fontSize:
1004
+ typeof lg.fontSize === 'number' ?
1005
+ lg.fontSize
1006
+ : parseInt(String(lg.fontSize ?? defaultTabsSizes.lg.fontSize), 10),
1007
+ borderRadius: lg.borderRadius ?? defaultTabsSizes.lg.borderRadius,
1008
+ fontWeight: lg.fontWeight ?? defaultTabsSizes.lg.fontWeight,
1009
+ },
1010
+ },
1011
+ };
1012
+ }
1013
+
1014
+ // SubTabs token types (aligned with components/subtabs.json)
1015
+ export type SubTabsSize = 'sm' | 'md';
1016
+
1017
+ export interface SubTabsSizeToken {
1018
+ height: string;
1019
+ padding: string;
1020
+ fontSize: string;
1021
+ borderRadius: string;
1022
+ iconSize: string;
1023
+ fontWeight: string;
1024
+ activeFontWeight: string;
1025
+ }
1026
+
1027
+ export interface SubTabsTokens {
1028
+ gap: string;
1029
+ trackPadding: string;
1030
+ trackBorderRadius: string;
1031
+ trackBackground: string;
1032
+ trackBorder: string;
1033
+ fontWeight: string;
1034
+ activeFontWeight: string;
1035
+ colors: Record<string, Record<string, string>>;
1036
+ }
1037
+
1038
+ const subTabsComp = (components as Record<string, any>).subTabs;
1039
+ export const SUB_TABS_TOKENS: SubTabsTokens = {
1040
+ gap: subTabsComp?.gap ?? '8px',
1041
+ trackPadding: subTabsComp?.trackPadding ?? '6px',
1042
+ trackBorderRadius: subTabsComp?.trackBorderRadius ?? '12px',
1043
+ trackBackground: subTabsComp?.trackBackground ?? '#F3F5F6',
1044
+ trackBorder: subTabsComp?.trackBorder ?? '#E5E7EB',
1045
+ fontWeight: subTabsComp?.fontWeight ?? '500',
1046
+ activeFontWeight: subTabsComp?.activeFontWeight ?? '600',
1047
+ colors: subTabsComp?.colors ?? {},
1048
+ };
1049
+
1050
+ export const SUB_TABS_SIZES: Record<SubTabsSize, SubTabsSizeToken> = {
1051
+ sm: {
1052
+ height: subTabsComp?.size?.sm?.height ?? '32px',
1053
+ padding: subTabsComp?.size?.sm?.padding ?? '6px 12px',
1054
+ fontSize: subTabsComp?.size?.sm?.fontSize ?? '12px',
1055
+ borderRadius: subTabsComp?.size?.sm?.borderRadius ?? '8px',
1056
+ iconSize: subTabsComp?.size?.sm?.iconSize ?? '22px',
1057
+ fontWeight: subTabsComp?.fontWeight ?? '500',
1058
+ activeFontWeight: subTabsComp?.activeFontWeight ?? '600',
1059
+ },
1060
+ md: {
1061
+ height: subTabsComp?.size?.md?.height ?? '40px',
1062
+ padding: subTabsComp?.size?.md?.padding ?? '8px 16px',
1063
+ fontSize: subTabsComp?.size?.md?.fontSize ?? '14px',
1064
+ borderRadius: subTabsComp?.size?.md?.borderRadius ?? '8px',
1065
+ iconSize: subTabsComp?.size?.md?.iconSize ?? '26px',
1066
+ fontWeight: subTabsComp?.fontWeight ?? '500',
1067
+ activeFontWeight: subTabsComp?.activeFontWeight ?? '600',
1068
+ },
1069
+ };
1070
+
1071
+ export interface SubTabsTokensFromResolved {
1072
+ tokens: SubTabsTokens;
1073
+ sizes: Record<SubTabsSize, SubTabsSizeToken>;
1074
+ }
1075
+
1076
+ const defaultSubTabsTokens = SUB_TABS_TOKENS;
1077
+ const defaultSubTabsSizes = SUB_TABS_SIZES;
1078
+
1079
+ export function getSubTabsTokensFromResolved(resolved: ResolvedTokens): SubTabsTokensFromResolved {
1080
+ const t = resolved.components?.subTabs as Record<string, any> | undefined;
1081
+ if (!t) {
1082
+ return { tokens: defaultSubTabsTokens, sizes: defaultSubTabsSizes };
1083
+ }
1084
+
1085
+ const sm = t.size?.sm ?? {};
1086
+ const md = t.size?.md ?? {};
1087
+
1088
+ return {
1089
+ tokens: {
1090
+ gap: t.gap ?? defaultSubTabsTokens.gap,
1091
+ trackPadding: t.trackPadding ?? defaultSubTabsTokens.trackPadding,
1092
+ trackBorderRadius: t.trackBorderRadius ?? defaultSubTabsTokens.trackBorderRadius,
1093
+ trackBackground: t.trackBackground ?? defaultSubTabsTokens.trackBackground,
1094
+ trackBorder: t.trackBorder ?? defaultSubTabsTokens.trackBorder,
1095
+ fontWeight: t.fontWeight ?? defaultSubTabsTokens.fontWeight,
1096
+ activeFontWeight: t.activeFontWeight ?? defaultSubTabsTokens.activeFontWeight,
1097
+ colors: t.colors ?? defaultSubTabsTokens.colors,
1098
+ },
1099
+ sizes: {
1100
+ sm: {
1101
+ height: sm.height ?? defaultSubTabsSizes.sm.height,
1102
+ padding: sm.padding ?? defaultSubTabsSizes.sm.padding,
1103
+ fontSize: sm.fontSize ?? defaultSubTabsSizes.sm.fontSize,
1104
+ borderRadius: sm.borderRadius ?? defaultSubTabsSizes.sm.borderRadius,
1105
+ iconSize: sm.iconSize ?? defaultSubTabsSizes.sm.iconSize,
1106
+ fontWeight: t.fontWeight ?? defaultSubTabsSizes.sm.fontWeight,
1107
+ activeFontWeight: t.activeFontWeight ?? defaultSubTabsSizes.sm.activeFontWeight,
1108
+ },
1109
+ md: {
1110
+ height: md.height ?? defaultSubTabsSizes.md.height,
1111
+ padding: md.padding ?? defaultSubTabsSizes.md.padding,
1112
+ fontSize: md.fontSize ?? defaultSubTabsSizes.md.fontSize,
1113
+ borderRadius: md.borderRadius ?? defaultSubTabsSizes.md.borderRadius,
1114
+ iconSize: md.iconSize ?? defaultSubTabsSizes.md.iconSize,
1115
+ fontWeight: t.fontWeight ?? defaultSubTabsSizes.md.fontWeight,
1116
+ activeFontWeight: t.activeFontWeight ?? defaultSubTabsSizes.md.activeFontWeight,
1117
+ },
1118
+ },
1119
+ };
1120
+ }
1121
+
1122
+ /* ---------------------------------- */
1123
+ /* Breadcrumb tokens */
1124
+ /* ---------------------------------- */
1125
+
1126
+ export interface BreadcrumbTokens {
1127
+ itemColor: string;
1128
+ currentColor: string;
1129
+ separatorColor: string;
1130
+ fontFamily: string;
1131
+ fontSize: string;
1132
+ fontWeight: string;
1133
+ itemFontWeight: string;
1134
+ lineHeight: string;
1135
+ gap: string;
1136
+ }
1137
+
1138
+ const breadcrumbComp = components.breadcrumb as Record<string, any> | undefined;
1139
+ export const BREADCRUMB_TOKENS: BreadcrumbTokens = {
1140
+ itemColor: breadcrumbComp?.itemColor ?? '#1f1f1f',
1141
+ currentColor: breadcrumbComp?.currentColor ?? '#1f1f1f',
1142
+ separatorColor: breadcrumbComp?.separatorColor ?? '#1f1f1f',
1143
+ fontFamily: breadcrumbComp?.fontFamily ?? '"Inter", sans-serif',
1144
+ fontSize: breadcrumbComp?.fontSize ?? '14px',
1145
+ fontWeight: breadcrumbComp?.fontWeight ?? '400',
1146
+ itemFontWeight: breadcrumbComp?.itemFontWeight ?? '600',
1147
+ lineHeight: breadcrumbComp?.lineHeight ?? '24px',
1148
+ gap: breadcrumbComp?.gap ?? '8px',
1149
+ };
1150
+
1151
+ const defaultBreadcrumbTokens = BREADCRUMB_TOKENS;
1152
+
1153
+ /** Build Breadcrumb tokens from resolved tokens. */
1154
+ export function getBreadcrumbTokensFromResolved(resolved: ResolvedTokens): BreadcrumbTokens {
1155
+ const t = resolved.components?.breadcrumb as Record<string, any> | undefined;
1156
+ if (!t) return defaultBreadcrumbTokens;
1157
+ return {
1158
+ itemColor: t.itemColor ?? defaultBreadcrumbTokens.itemColor,
1159
+ currentColor: t.currentColor ?? defaultBreadcrumbTokens.currentColor,
1160
+ separatorColor: t.separatorColor ?? defaultBreadcrumbTokens.separatorColor,
1161
+ fontFamily: t.fontFamily ?? defaultBreadcrumbTokens.fontFamily,
1162
+ fontSize: t.fontSize ?? defaultBreadcrumbTokens.fontSize,
1163
+ fontWeight: t.fontWeight ?? defaultBreadcrumbTokens.fontWeight,
1164
+ itemFontWeight: t.itemFontWeight ?? defaultBreadcrumbTokens.itemFontWeight,
1165
+ lineHeight: t.lineHeight ?? defaultBreadcrumbTokens.lineHeight,
1166
+ gap: t.gap ?? defaultBreadcrumbTokens.gap,
1167
+ };
1168
+ }
1169
+
1170
+ export interface ToggleButtonTokens {
1171
+ gap: string;
1172
+ fontFamily: string;
1173
+ lineHeight: string;
1174
+ fontWeight: string | number;
1175
+ activeFontWeight: string | number;
1176
+ sizeSmHeight: string;
1177
+ sizeSmPaddingX: string;
1178
+ sizeSmPaddingY: string;
1179
+ sizeSmFontSize: string;
1180
+ sizeSmBorderRadius: string;
1181
+ sizeSmBorderWidth: string;
1182
+ sizeMdHeight: string;
1183
+ sizeMdPaddingX: string;
1184
+ sizeMdPaddingY: string;
1185
+ sizeMdFontSize: string;
1186
+ sizeMdBorderRadius: string;
1187
+ sizeMdBorderWidth: string;
1188
+ sizeLgHeight: string;
1189
+ sizeLgPaddingX: string;
1190
+ sizeLgPaddingY: string;
1191
+ sizeLgFontSize: string;
1192
+ sizeLgBorderRadius: string;
1193
+ sizeLgBorderWidth: string;
1194
+ inactive: {
1195
+ background: string;
1196
+ backgroundWhite: string;
1197
+ color: string;
1198
+ border: string;
1199
+ };
1200
+ active: {
1201
+ background: string;
1202
+ color: string;
1203
+ border: string;
1204
+ };
1205
+ }
1206
+
1207
+ const toggleButtonComp = components.togglebutton as Record<string, any> | undefined;
1208
+ export const TOGGLE_BUTTON_TOKENS: ToggleButtonTokens = {
1209
+ gap: toggleButtonComp?.gap ?? '4px',
1210
+ fontFamily: toggleButtonComp?.button?.fontFamily ?? '"Inter", sans-serif',
1211
+ lineHeight: toggleButtonComp?.button?.lineHeight ?? '18px',
1212
+ fontWeight: toggleButtonComp?.button?.fontWeight ?? '500',
1213
+ activeFontWeight: toggleButtonComp?.button?.activeFontWeight ?? '600',
1214
+ sizeSmHeight: toggleButtonComp?.size?.sm?.height ?? '28px',
1215
+ sizeSmPaddingX: toggleButtonComp?.size?.sm?.paddingX ?? '8px',
1216
+ sizeSmPaddingY: toggleButtonComp?.size?.sm?.paddingY ?? '4px',
1217
+ sizeSmFontSize: toggleButtonComp?.size?.sm?.fontSize ?? '12px',
1218
+ sizeSmBorderRadius: toggleButtonComp?.size?.sm?.borderRadius ?? '8px',
1219
+ sizeSmBorderWidth: toggleButtonComp?.size?.sm?.borderWidth ?? '1px',
1220
+ sizeMdHeight: toggleButtonComp?.size?.md?.height ?? '40px',
1221
+ sizeMdPaddingX: toggleButtonComp?.size?.md?.paddingX ?? '12px',
1222
+ sizeMdPaddingY: toggleButtonComp?.size?.md?.paddingY ?? '6px',
1223
+ sizeMdFontSize: toggleButtonComp?.size?.md?.fontSize ?? '14px',
1224
+ sizeMdBorderRadius: toggleButtonComp?.size?.md?.borderRadius ?? '12px',
1225
+ sizeMdBorderWidth: toggleButtonComp?.size?.md?.borderWidth ?? '1px',
1226
+ sizeLgHeight: toggleButtonComp?.size?.lg?.height ?? '48px',
1227
+ sizeLgPaddingX: toggleButtonComp?.size?.lg?.paddingX ?? '16px',
1228
+ sizeLgPaddingY: toggleButtonComp?.size?.lg?.paddingY ?? '8px',
1229
+ sizeLgFontSize: toggleButtonComp?.size?.lg?.fontSize ?? '16px',
1230
+ sizeLgBorderRadius: toggleButtonComp?.size?.lg?.borderRadius ?? '12px',
1231
+ sizeLgBorderWidth: toggleButtonComp?.size?.lg?.borderWidth ?? '1px',
1232
+ inactive: {
1233
+ background: toggleButtonComp?.inactive?.background ?? '#ffffff',
1234
+ backgroundWhite:
1235
+ toggleButtonComp?.inactive?.backgroundWhite ??
1236
+ toggleButtonComp?.inactive?.background ??
1237
+ '#ffffff',
1238
+ color: toggleButtonComp?.inactive?.color ?? '#1f1f1f',
1239
+ border: toggleButtonComp?.inactive?.border ?? '#c8d0d5',
1240
+ },
1241
+ active: {
1242
+ background: toggleButtonComp?.active?.background ?? '#00545e',
1243
+ color: toggleButtonComp?.active?.color ?? '#ffffff',
1244
+ border: toggleButtonComp?.active?.border ?? '#00545e',
1245
+ },
1246
+ };
1247
+
1248
+ const defaultToggleButtonTokens = TOGGLE_BUTTON_TOKENS;
1249
+
1250
+ /** Build ToggleButton tokens from resolved tokens (mode + brand). */
1251
+ export function getToggleButtonTokensFromResolved(resolved: ResolvedTokens): ToggleButtonTokens {
1252
+ const t = resolved.components?.togglebutton as Record<string, any> | undefined;
1253
+ if (!t) return defaultToggleButtonTokens;
1254
+ return {
1255
+ gap: t.gap ?? defaultToggleButtonTokens.gap,
1256
+ fontFamily: t.button?.fontFamily ?? defaultToggleButtonTokens.fontFamily,
1257
+ lineHeight: t.button?.lineHeight ?? defaultToggleButtonTokens.lineHeight,
1258
+ fontWeight: t.button?.fontWeight ?? defaultToggleButtonTokens.fontWeight,
1259
+ activeFontWeight: t.button?.activeFontWeight ?? defaultToggleButtonTokens.activeFontWeight,
1260
+ sizeSmHeight: t.size?.sm?.height ?? defaultToggleButtonTokens.sizeSmHeight,
1261
+ sizeSmPaddingX: t.size?.sm?.paddingX ?? defaultToggleButtonTokens.sizeSmPaddingX,
1262
+ sizeSmPaddingY: t.size?.sm?.paddingY ?? defaultToggleButtonTokens.sizeSmPaddingY,
1263
+ sizeSmFontSize: t.size?.sm?.fontSize ?? defaultToggleButtonTokens.sizeSmFontSize,
1264
+ sizeSmBorderRadius: t.size?.sm?.borderRadius ?? defaultToggleButtonTokens.sizeSmBorderRadius,
1265
+ sizeSmBorderWidth: t.size?.sm?.borderWidth ?? defaultToggleButtonTokens.sizeSmBorderWidth,
1266
+ sizeMdHeight: t.size?.md?.height ?? defaultToggleButtonTokens.sizeMdHeight,
1267
+ sizeMdPaddingX: t.size?.md?.paddingX ?? defaultToggleButtonTokens.sizeMdPaddingX,
1268
+ sizeMdPaddingY: t.size?.md?.paddingY ?? defaultToggleButtonTokens.sizeMdPaddingY,
1269
+ sizeMdFontSize: t.size?.md?.fontSize ?? defaultToggleButtonTokens.sizeMdFontSize,
1270
+ sizeMdBorderRadius: t.size?.md?.borderRadius ?? defaultToggleButtonTokens.sizeMdBorderRadius,
1271
+ sizeMdBorderWidth: t.size?.md?.borderWidth ?? defaultToggleButtonTokens.sizeMdBorderWidth,
1272
+ sizeLgHeight: t.size?.lg?.height ?? defaultToggleButtonTokens.sizeLgHeight,
1273
+ sizeLgPaddingX: t.size?.lg?.paddingX ?? defaultToggleButtonTokens.sizeLgPaddingX,
1274
+ sizeLgPaddingY: t.size?.lg?.paddingY ?? defaultToggleButtonTokens.sizeLgPaddingY,
1275
+ sizeLgFontSize: t.size?.lg?.fontSize ?? defaultToggleButtonTokens.sizeLgFontSize,
1276
+ sizeLgBorderRadius: t.size?.lg?.borderRadius ?? defaultToggleButtonTokens.sizeLgBorderRadius,
1277
+ sizeLgBorderWidth: t.size?.lg?.borderWidth ?? defaultToggleButtonTokens.sizeLgBorderWidth,
1278
+ inactive: {
1279
+ background: t.inactive?.background ?? defaultToggleButtonTokens.inactive.background,
1280
+ backgroundWhite:
1281
+ t.inactive?.backgroundWhite ??
1282
+ t.inactive?.background ??
1283
+ defaultToggleButtonTokens.inactive.backgroundWhite,
1284
+ color: t.inactive?.color ?? defaultToggleButtonTokens.inactive.color,
1285
+ border: t.inactive?.border ?? defaultToggleButtonTokens.inactive.border,
1286
+ },
1287
+ active: {
1288
+ background: t.active?.background ?? defaultToggleButtonTokens.active.background,
1289
+ color: t.active?.color ?? defaultToggleButtonTokens.active.color,
1290
+ border: t.active?.border ?? defaultToggleButtonTokens.active.border,
1291
+ },
1292
+ };
1293
+ }
1294
+
1295
+ export interface SwitchTokens {
1296
+ sizeSmWidth: string;
1297
+ sizeSmHeight: string;
1298
+ sizeSmThumbSize: string;
1299
+ sizeSmTrackPadding: string;
1300
+ sizeSmRadius: string;
1301
+ sizeSmBorderWidth: string;
1302
+ sizeMdWidth: string;
1303
+ sizeMdHeight: string;
1304
+ sizeMdThumbSize: string;
1305
+ sizeMdTrackPadding: string;
1306
+ sizeMdRadius: string;
1307
+ sizeMdBorderWidth: string;
1308
+ sizeLgWidth: string;
1309
+ sizeLgHeight: string;
1310
+ sizeLgThumbSize: string;
1311
+ sizeLgTrackPadding: string;
1312
+ sizeLgRadius: string;
1313
+ sizeLgBorderWidth: string;
1314
+ trackBackground: string;
1315
+ trackBorder: string;
1316
+ trackBackgroundActive: string;
1317
+ trackBorderActive: string;
1318
+ thumbBackground: string;
1319
+ thumbBackgroundActive: string;
1320
+ labelFontSize: string;
1321
+ labelFontWeight: number;
1322
+ labelColor: string;
1323
+ labelFontFamily: string;
1324
+ labelMarginBottom: string;
1325
+ captionFontSize: string;
1326
+ captionTextColor: string;
1327
+ switchHelperTextFontSize: string;
1328
+ switchHelperTextColor: string;
1329
+ }
1330
+
1331
+ const switchComp = components.switch as Record<string, any> | undefined;
1332
+ export const SWITCH_TOKENS: SwitchTokens = {
1333
+ sizeSmWidth: switchComp?.size?.sm?.width ?? '52px',
1334
+ sizeSmHeight: switchComp?.size?.sm?.height ?? '32px',
1335
+ sizeSmThumbSize: switchComp?.size?.sm?.thumbSize ?? '28px',
1336
+ sizeSmTrackPadding: switchComp?.size?.sm?.trackPadding ?? '2px',
1337
+ sizeSmRadius: switchComp?.size?.sm?.radius ?? '9999px',
1338
+ sizeSmBorderWidth: switchComp?.size?.sm?.borderWidth ?? '1px',
1339
+ sizeMdWidth: switchComp?.size?.md?.width ?? '52px',
1340
+ sizeMdHeight: switchComp?.size?.md?.height ?? '32px',
1341
+ sizeMdThumbSize: switchComp?.size?.md?.thumbSize ?? '28px',
1342
+ sizeMdTrackPadding: switchComp?.size?.md?.trackPadding ?? '2px',
1343
+ sizeMdRadius: switchComp?.size?.md?.radius ?? '9999px',
1344
+ sizeMdBorderWidth: switchComp?.size?.md?.borderWidth ?? '1px',
1345
+ sizeLgWidth: switchComp?.size?.lg?.width ?? '52px',
1346
+ sizeLgHeight: switchComp?.size?.lg?.height ?? '32px',
1347
+ sizeLgThumbSize: switchComp?.size?.lg?.thumbSize ?? '28px',
1348
+ sizeLgTrackPadding: switchComp?.size?.lg?.trackPadding ?? '2px',
1349
+ sizeLgRadius: switchComp?.size?.lg?.radius ?? '9999px',
1350
+ sizeLgBorderWidth: switchComp?.size?.lg?.borderWidth ?? '1px',
1351
+ trackBackground: switchComp?.track?.background ?? '#ffffff',
1352
+ trackBorder: switchComp?.track?.border ?? '#1f1f1f',
1353
+ trackBackgroundActive: switchComp?.track?.backgroundActive ?? '#00545e',
1354
+ trackBorderActive: switchComp?.track?.borderActive ?? '#00545e',
1355
+ thumbBackground: switchComp?.thumb?.background ?? '#1f1f1f',
1356
+ thumbBackgroundActive: switchComp?.thumb?.backgroundActive ?? '#ffffff',
1357
+ labelFontSize: switchComp?.label?.fontSize ?? '14px',
1358
+ labelFontWeight: switchComp?.label?.fontWeight ?? 500,
1359
+ labelColor: switchComp?.label?.color ?? '#1f1f1f',
1360
+ labelFontFamily: switchComp?.label?.fontFamily ?? 'Inter',
1361
+ labelMarginBottom: switchComp?.label?.marginBottom ?? '8px',
1362
+ captionFontSize: switchComp?.caption?.fontSize ?? '12px',
1363
+ captionTextColor: switchComp?.caption?.textColor ?? '#1f1f1f',
1364
+ switchHelperTextFontSize: switchComp?.helper?.textFontSize ?? '12px',
1365
+ switchHelperTextColor: switchComp?.helper?.textColor ?? '#1f1f1f',
1366
+ };
1367
+
1368
+ const defaultSwitchTokens = SWITCH_TOKENS;
1369
+
1370
+ /** Build Switch tokens from resolved tokens (mode + brand). */
1371
+ export function getSwitchTokensFromResolved(resolved: ResolvedTokens): SwitchTokens {
1372
+ const t = resolved.components?.switch as Record<string, any> | undefined;
1373
+ if (!t) return defaultSwitchTokens;
1374
+ return {
1375
+ sizeSmWidth: t.size?.sm?.width ?? defaultSwitchTokens.sizeSmWidth,
1376
+ sizeSmHeight: t.size?.sm?.height ?? defaultSwitchTokens.sizeSmHeight,
1377
+ sizeSmThumbSize: t.size?.sm?.thumbSize ?? defaultSwitchTokens.sizeSmThumbSize,
1378
+ sizeSmTrackPadding: t.size?.sm?.trackPadding ?? defaultSwitchTokens.sizeSmTrackPadding,
1379
+ sizeSmRadius: t.size?.sm?.radius ?? defaultSwitchTokens.sizeSmRadius,
1380
+ sizeSmBorderWidth: t.size?.sm?.borderWidth ?? defaultSwitchTokens.sizeSmBorderWidth,
1381
+ sizeMdWidth: t.size?.md?.width ?? defaultSwitchTokens.sizeMdWidth,
1382
+ sizeMdHeight: t.size?.md?.height ?? defaultSwitchTokens.sizeMdHeight,
1383
+ sizeMdThumbSize: t.size?.md?.thumbSize ?? defaultSwitchTokens.sizeMdThumbSize,
1384
+ sizeMdTrackPadding: t.size?.md?.trackPadding ?? defaultSwitchTokens.sizeMdTrackPadding,
1385
+ sizeMdRadius: t.size?.md?.radius ?? defaultSwitchTokens.sizeMdRadius,
1386
+ sizeMdBorderWidth: t.size?.md?.borderWidth ?? defaultSwitchTokens.sizeMdBorderWidth,
1387
+ sizeLgWidth: t.size?.lg?.width ?? defaultSwitchTokens.sizeLgWidth,
1388
+ sizeLgHeight: t.size?.lg?.height ?? defaultSwitchTokens.sizeLgHeight,
1389
+ sizeLgThumbSize: t.size?.lg?.thumbSize ?? defaultSwitchTokens.sizeLgThumbSize,
1390
+ sizeLgTrackPadding: t.size?.lg?.trackPadding ?? defaultSwitchTokens.sizeLgTrackPadding,
1391
+ sizeLgRadius: t.size?.lg?.radius ?? defaultSwitchTokens.sizeLgRadius,
1392
+ sizeLgBorderWidth: t.size?.lg?.borderWidth ?? defaultSwitchTokens.sizeLgBorderWidth,
1393
+ trackBackground: t.track?.background ?? defaultSwitchTokens.trackBackground,
1394
+ trackBorder: t.track?.border ?? defaultSwitchTokens.trackBorder,
1395
+ trackBackgroundActive: t.track?.backgroundActive ?? defaultSwitchTokens.trackBackgroundActive,
1396
+ trackBorderActive: t.track?.borderActive ?? defaultSwitchTokens.trackBorderActive,
1397
+ thumbBackground: t.thumb?.background ?? defaultSwitchTokens.thumbBackground,
1398
+ thumbBackgroundActive: t.thumb?.backgroundActive ?? defaultSwitchTokens.thumbBackgroundActive,
1399
+ labelFontSize: t.label?.fontSize ?? defaultSwitchTokens.labelFontSize,
1400
+ labelFontWeight: t.label?.fontWeight ?? defaultSwitchTokens.labelFontWeight,
1401
+ labelColor: t.label?.color ?? defaultSwitchTokens.labelColor,
1402
+ labelFontFamily: t.label?.fontFamily ?? defaultSwitchTokens.labelFontFamily,
1403
+ labelMarginBottom: t.label?.marginBottom ?? defaultSwitchTokens.labelMarginBottom,
1404
+ captionFontSize: t.caption?.fontSize ?? defaultSwitchTokens.captionFontSize,
1405
+ captionTextColor: t.caption?.textColor ?? defaultSwitchTokens.captionTextColor,
1406
+ switchHelperTextFontSize:
1407
+ t.helper?.textFontSize ?? defaultSwitchTokens.switchHelperTextFontSize,
1408
+ switchHelperTextColor: t.helper?.textColor ?? defaultSwitchTokens.switchHelperTextColor,
1409
+ };
1410
+ }
1411
+
1412
+ // Menu token types (aligned with components/menu.json).
1413
+ // Use getMenuTokensFromResolved(resolved) when screen/theme changes (e.g. useTokens()).
1414
+ export interface MenuTokens {
1415
+ minWidth: string;
1416
+ borderRadius: string;
1417
+ padding: string;
1418
+ marginTop: string;
1419
+ itemPaddingY: string;
1420
+ itemPaddingX: string;
1421
+ itemIconLabelGap: string;
1422
+ itemBorderRadius: string;
1423
+ fontFamily: string;
1424
+ fontSize: number;
1425
+ fontWeight: number;
1426
+ lineHeight: string;
1427
+ textColor: string;
1428
+ selectedBackground: string;
1429
+ hoverBackground: string;
1430
+ background: string;
1431
+ listMaxHeight: string;
1432
+ }
1433
+
1434
+ const menuComp = components.menu as Record<string, unknown> | undefined;
1435
+ export const MENU_TOKENS: MenuTokens = {
1436
+ minWidth: (menuComp?.minWidth as string) ?? '180px',
1437
+ borderRadius: (menuComp?.borderRadius as string) ?? '16px',
1438
+ padding: (menuComp?.padding as string) ?? '12px',
1439
+ marginTop: (menuComp?.marginTop as string) ?? '12px',
1440
+ itemPaddingY: (menuComp?.itemPaddingY as string) ?? '12px',
1441
+ itemPaddingX: (menuComp?.itemPaddingX as string) ?? '16px',
1442
+ itemIconLabelGap: (menuComp?.itemIconLabelGap as string) ?? '12px',
1443
+ itemBorderRadius: (menuComp?.itemBorderRadius as string) ?? '6px',
1444
+ fontFamily: (menuComp?.fontFamily as string) ?? '"Inter", sans-serif',
1445
+ fontSize: parseInt(String(menuComp?.fontSize ?? 16), 10),
1446
+ fontWeight:
1447
+ typeof menuComp?.fontWeight === 'string' ?
1448
+ parseInt(menuComp.fontWeight as string, 10)
1449
+ : Number(menuComp?.fontWeight ?? 500),
1450
+ lineHeight: (menuComp?.lineHeight as string) ?? '24px',
1451
+ textColor: (menuComp?.textColor as string) ?? '#374151',
1452
+ selectedBackground: (menuComp?.selectedBackground as string) ?? '#F3F4F6',
1453
+ hoverBackground: (menuComp?.hoverBackground as string) ?? '#F9FAFB',
1454
+ background: (menuComp?.background as string) ?? '#FFFFFF',
1455
+ listMaxHeight: (menuComp?.listMaxHeight as string) ?? '320px',
1456
+ };
1457
+
1458
+ /** Build Menu tokens from resolved tokens (e.g. from useTokens()). Use when screen/theme changes so menu picks responsive size.menu.* */
1459
+ export function getMenuTokensFromResolved(resolved: ResolvedTokens): MenuTokens {
1460
+ const m = resolved.components?.menu as Record<string, unknown> | undefined;
1461
+ if (!m) return MENU_TOKENS;
1462
+ return {
1463
+ minWidth: (m.minWidth as string) ?? MENU_TOKENS.minWidth,
1464
+ borderRadius: (m.borderRadius as string) ?? MENU_TOKENS.borderRadius,
1465
+ padding: (m.padding as string) ?? MENU_TOKENS.padding,
1466
+ marginTop: (m.marginTop as string) ?? MENU_TOKENS.marginTop,
1467
+ itemPaddingY: (m.itemPaddingY as string) ?? MENU_TOKENS.itemPaddingY,
1468
+ itemPaddingX: (m.itemPaddingX as string) ?? MENU_TOKENS.itemPaddingX,
1469
+ itemIconLabelGap: (m.itemIconLabelGap as string) ?? MENU_TOKENS.itemIconLabelGap,
1470
+ itemBorderRadius: (m.itemBorderRadius as string) ?? MENU_TOKENS.itemBorderRadius,
1471
+ fontFamily: (m.fontFamily as string) ?? MENU_TOKENS.fontFamily,
1472
+ fontSize: parseInt(String(m.fontSize ?? MENU_TOKENS.fontSize), 10),
1473
+ fontWeight:
1474
+ typeof m.fontWeight === 'string' ?
1475
+ parseInt(m.fontWeight, 10)
1476
+ : Number(m.fontWeight ?? MENU_TOKENS.fontWeight),
1477
+ lineHeight: (m.lineHeight as string) ?? MENU_TOKENS.lineHeight,
1478
+ textColor: (m.textColor as string) ?? MENU_TOKENS.textColor,
1479
+ selectedBackground: (m.selectedBackground as string) ?? MENU_TOKENS.selectedBackground,
1480
+ hoverBackground: (m.hoverBackground as string) ?? MENU_TOKENS.hoverBackground,
1481
+ background: (m.background as string) ?? MENU_TOKENS.background,
1482
+ listMaxHeight: (m.listMaxHeight as string) ?? MENU_TOKENS.listMaxHeight,
1483
+ };
1484
+ }
1485
+
1486
+ // TextField tokens (legacy format; from components/textfield.json).
1487
+ // Textfield border is picked from outline.default/active/error, which resolve from color.border
1488
+ // (and color.error), so brands can override textfield border via brand.color.border in brand.json.
1489
+ export interface TextFieldTokens {
1490
+ height: string;
1491
+ heightSm: string;
1492
+ heightMd: string;
1493
+ heightLg: string;
1494
+ borderRadius: string;
1495
+ borderRadiusSm: string;
1496
+ borderRadiusMd: string;
1497
+ borderRadiusLg: string;
1498
+ fontFamily: string;
1499
+ fontSize: number;
1500
+ fontSizeSm: number;
1501
+ fontSizeMd: number;
1502
+ fontSizeLg: number;
1503
+ labelFontSize: number;
1504
+ labelFontWeight: number;
1505
+ labelMarginBottom: string;
1506
+ activeLabelFontSize: number;
1507
+ helperFontSize: number;
1508
+ labelColor: string;
1509
+ activeLabelColor: string;
1510
+ inputTextColor: string;
1511
+ placeholderColor: string;
1512
+ outlineDefault: string;
1513
+ outlineActive: string;
1514
+ outlineError: string;
1515
+ errorColor: string;
1516
+ backgroundColor: string;
1517
+ backgroundColorWhite: string;
1518
+ disabledOpacity: string;
1519
+ paddingSm: string;
1520
+ paddingMd: string;
1521
+ paddingLg: string;
1522
+ captionFontSize: number;
1523
+ captionTextColor: string;
1524
+ /** Left padding applied to the input when a start/end icon is present. Sourced from spacing.9 (48px). */
1525
+ iconPaddingLeft: string;
1526
+ /** Absolute left offset of the start adornment icon. Sourced from spacing.4 (16px). */
1527
+ iconStartLeft: string;
1528
+ /** Absolute right offset of the end adornment icon. Sourced from spacing.2 (8px). */
1529
+ iconEndRight: string;
1530
+ }
1531
+
1532
+ const tfSize = components.textfield.size;
1533
+ export const TEXT_FIELD_TOKENS: TextFieldTokens = {
1534
+ height: tfSize.md.height,
1535
+ heightSm: tfSize.sm.height,
1536
+ heightMd: tfSize.md.height,
1537
+ heightLg: tfSize.lg.height,
1538
+ borderRadius: tfSize.md.borderRadius,
1539
+ borderRadiusSm: tfSize.sm.borderRadius,
1540
+ borderRadiusMd: tfSize.md.borderRadius,
1541
+ borderRadiusLg: tfSize.lg.borderRadius,
1542
+ fontFamily: components.textfield.fontFamily,
1543
+ fontSize: parseInt(tfSize.md.fontSize),
1544
+ fontSizeSm: parseInt(tfSize.sm.fontSize),
1545
+ fontSizeMd: parseInt(tfSize.md.fontSize),
1546
+ fontSizeLg: parseInt(tfSize.lg.fontSize),
1547
+ labelFontSize: parseInt(components.textfield.label.fontSize),
1548
+ labelFontWeight:
1549
+ typeof components.textfield.label.fontWeight === 'string' ?
1550
+ parseInt(components.textfield.label.fontWeight)
1551
+ : components.textfield.label.fontWeight,
1552
+ labelMarginBottom: components.textfield.label.marginBottom,
1553
+ activeLabelFontSize: parseInt(components.textfield.label.activeFontSize),
1554
+ helperFontSize: parseInt(components.textfield.helper.fontSize),
1555
+ labelColor: components.textfield.label.color,
1556
+ activeLabelColor: components.textfield.label.activeColor,
1557
+ inputTextColor: components.textfield.textColor,
1558
+ placeholderColor: components.textfield.placeholderColor,
1559
+ outlineDefault: components.textfield.outline.default,
1560
+ outlineActive: components.textfield.outline.active,
1561
+ outlineError: components.textfield.outline.error,
1562
+ errorColor: components.textfield.error.color,
1563
+ backgroundColor: components.textfield.background,
1564
+ backgroundColorWhite: components.textfield.backgroundWhite ?? components.textfield.background,
1565
+ disabledOpacity: components.textfield.disabledOpacity,
1566
+ paddingSm: tfSize.sm.padding,
1567
+ paddingMd: tfSize.md.padding,
1568
+ paddingLg: tfSize.lg.padding,
1569
+ captionFontSize: components.textfield.caption.fontSize,
1570
+ captionTextColor: components.textfield.caption.textColor,
1571
+ iconPaddingLeft: components.textfield.iconPaddingLeft ?? '48px',
1572
+ iconStartLeft: (components.textfield.icon as any)?.startLeft ?? '16px',
1573
+ iconEndRight: (components.textfield.icon as any)?.endRight ?? '8px',
1574
+ };
1575
+
1576
+ export interface IpWhitelistFieldTokens {
1577
+ labelMarginTop: string;
1578
+ labelColor: string;
1579
+ requiredColor: string;
1580
+ requiredGap: string;
1581
+ rowMarginTop: string;
1582
+ groupGap: string;
1583
+ buttonsGap: string;
1584
+ width: string;
1585
+ }
1586
+
1587
+ export const IPWHITELISTFIELD_TOKENS: IpWhitelistFieldTokens = {
1588
+ labelMarginTop: (components.ipwhitelistfield as any)?.label?.marginTop,
1589
+ labelColor: (components.ipwhitelistfield as any)?.label?.color,
1590
+ requiredColor: (components.ipwhitelistfield as any)?.label?.requiredColor,
1591
+ requiredGap: (components.ipwhitelistfield as any)?.label?.requiredGap,
1592
+ rowMarginTop: (components.ipwhitelistfield as any)?.row?.marginTop,
1593
+ groupGap: (components.ipwhitelistfield as any)?.row?.groupGap,
1594
+ buttonsGap: (components.ipwhitelistfield as any)?.row?.buttonsGap,
1595
+ width: (components.ipwhitelistfield as any)?.row?.width,
1596
+ };
1597
+
1598
+ export function getIpWhitelistFieldTokensFromResolved(
1599
+ resolved: ResolvedTokens
1600
+ ): IpWhitelistFieldTokens {
1601
+ const ipw = (resolved.components as any)?.ipwhitelistfield;
1602
+ if (!ipw) return IPWHITELISTFIELD_TOKENS;
1603
+ return {
1604
+ labelMarginTop: ipw.label?.marginTop ?? IPWHITELISTFIELD_TOKENS.labelMarginTop,
1605
+ labelColor: ipw.label?.color ?? IPWHITELISTFIELD_TOKENS.labelColor,
1606
+ requiredColor: ipw.label?.requiredColor ?? IPWHITELISTFIELD_TOKENS.requiredColor,
1607
+ requiredGap: ipw.label?.requiredGap ?? IPWHITELISTFIELD_TOKENS.requiredGap,
1608
+ rowMarginTop: ipw.row?.marginTop ?? IPWHITELISTFIELD_TOKENS.rowMarginTop,
1609
+ groupGap: ipw.row?.groupGap ?? IPWHITELISTFIELD_TOKENS.groupGap,
1610
+ buttonsGap: ipw.row?.buttonsGap ?? IPWHITELISTFIELD_TOKENS.buttonsGap,
1611
+ width: ipw.row?.otpWidth ?? IPWHITELISTFIELD_TOKENS.width,
1612
+ };
1613
+ }
1614
+
1615
+ /** Build TextField tokens from resolved tokens (e.g. from useTokens()). Use this so padding/height update when screen changes. */
1616
+ export function getTextFieldTokensFromResolved(resolved: ResolvedTokens): TextFieldTokens {
1617
+ const tf = resolved.components?.textfield;
1618
+ if (!tf?.size) return TEXT_FIELD_TOKENS;
1619
+ const sm = tf.size.sm as any;
1620
+ const md = tf.size.md as any;
1621
+ const lg = tf.size.lg as any;
1622
+ return {
1623
+ height: md?.height ?? '40px',
1624
+ heightSm: sm?.height ?? '28px',
1625
+ heightMd: md?.height ?? '40px',
1626
+ heightLg: lg?.height ?? '48px',
1627
+ borderRadius: md?.borderRadius ?? '8px',
1628
+ borderRadiusSm: sm?.borderRadius ?? '6px',
1629
+ borderRadiusMd: md?.borderRadius ?? '8px',
1630
+ borderRadiusLg: lg?.borderRadius ?? '8px',
1631
+ fontFamily: tf.fontFamily ?? 'inherit',
1632
+ fontSize: parseInt(String(md?.fontSize ?? 16)),
1633
+ fontSizeSm: parseInt(String(sm?.fontSize ?? 14)),
1634
+ fontSizeMd: parseInt(String(md?.fontSize ?? 16)),
1635
+ fontSizeLg: parseInt(String(lg?.fontSize ?? 18)),
1636
+ labelFontSize: parseInt(String(tf.label?.fontSize ?? 14)),
1637
+ labelFontWeight:
1638
+ typeof tf.label?.fontWeight === 'string' ?
1639
+ parseInt(tf.label.fontWeight)
1640
+ : Number(tf.label?.fontWeight ?? 500),
1641
+ labelMarginBottom: tf.label?.marginBottom ?? '8px',
1642
+ activeLabelFontSize: parseInt(String(tf.label?.activeFontSize ?? 14)),
1643
+ helperFontSize: parseInt(String(tf.helper?.fontSize ?? 12)),
1644
+ labelColor: tf.label?.color ?? '#000',
1645
+ activeLabelColor: tf.label?.activeColor ?? '#000',
1646
+ inputTextColor: tf.textColor ?? '#000',
1647
+ placeholderColor: tf.placeholderColor ?? '#999',
1648
+ outlineDefault: tf.outline?.default ?? 'transparent',
1649
+ outlineActive: tf.outline?.active ?? '#0066cc',
1650
+ outlineError: tf.outline?.error ?? '#c00',
1651
+ errorColor: tf.error?.color ?? '#c00',
1652
+ backgroundColor: tf.background ?? '#fff',
1653
+ backgroundColorWhite: tf.backgroundWhite ?? '#fff',
1654
+ disabledOpacity: tf.disabledOpacity ?? '0.5',
1655
+ paddingSm: sm?.padding ?? '4px 8px',
1656
+ paddingMd: md?.padding ?? '6px 12px',
1657
+ paddingLg: lg?.padding ?? '8px 16px',
1658
+ captionFontSize: tf.caption?.fontSize ?? 12,
1659
+ captionTextColor: tf.caption?.textColor ?? '#000',
1660
+ iconPaddingLeft: tf.iconPaddingLeft ?? '48px',
1661
+ iconStartLeft: (tf.icon as any)?.startLeft ?? '16px',
1662
+ iconEndRight: (tf.icon as any)?.endRight ?? '8px',
1663
+ };
1664
+ }
1665
+
1666
+ /** Build Radio tokens from resolved tokens (e.g. from useTokens()). Use this so tokens update when screen changes. */
1667
+ export function getRadioTokensFromResolved(resolved: ResolvedTokens): RadioTokens {
1668
+ const radio = resolved.components?.radio;
1669
+ if (!radio) return RADIO_TOKENS;
1670
+ return {
1671
+ headingFontSize: radio.heading?.fontSize ?? RADIO_TOKENS.headingFontSize,
1672
+ headingFontWeight:
1673
+ typeof radio.heading?.fontWeight === 'string' ? parseInt(radio.heading.fontWeight)
1674
+ : typeof radio.heading?.fontWeight === 'number' ? radio.heading.fontWeight
1675
+ : RADIO_TOKENS.headingFontWeight,
1676
+ headingFontFamily: radio.heading?.fontFamily ?? RADIO_TOKENS.headingFontFamily,
1677
+ headingColor: radio.heading?.color ?? RADIO_TOKENS.headingColor,
1678
+ headingMarginBottom: radio.heading?.marginBottom ?? RADIO_TOKENS.headingMarginBottom,
1679
+ labelFontSize: radio.label?.fontSize ?? RADIO_TOKENS.labelFontSize,
1680
+ labelFontWeight:
1681
+ typeof radio.label?.fontWeight === 'string' ? parseInt(radio.label.fontWeight)
1682
+ : typeof radio.label?.fontWeight === 'number' ? radio.label.fontWeight
1683
+ : RADIO_TOKENS.labelFontWeight,
1684
+ labelFontFamily: radio.label?.fontFamily ?? RADIO_TOKENS.labelFontFamily,
1685
+ labelColorActive: radio.label?.color?.active ?? RADIO_TOKENS.labelColorActive,
1686
+ labelColorInactive: radio.label?.color?.inactive ?? RADIO_TOKENS.labelColorInactive,
1687
+ labelMarginLeft: radio.label?.marginLeft ?? RADIO_TOKENS.labelMarginLeft,
1688
+ radioSize: radio.radio?.size ?? RADIO_TOKENS.radioSize,
1689
+ radioDotSize:
1690
+ (radio.size as { md?: { dotSize?: string } })?.md?.dotSize ??
1691
+ (radio.size as { sm?: { dotSize?: string } })?.sm?.dotSize ??
1692
+ RADIO_TOKENS.radioDotSize,
1693
+ radioBorderRadius: radio.radio?.borderRadius ?? RADIO_TOKENS.radioBorderRadius,
1694
+ radioStrokeWidth: radio.radio?.strokeWidth ?? RADIO_TOKENS.radioStrokeWidth,
1695
+ radioColorActive: radio.radio?.color?.active ?? RADIO_TOKENS.radioColorActive,
1696
+ radioColorInactive: radio.radio?.color?.inactive ?? RADIO_TOKENS.radioColorInactive,
1697
+ radioColorDot: (radio.radio?.color as { dot?: string })?.dot ?? RADIO_TOKENS.radioColorDot,
1698
+ radioBackground: radio.radio?.background ?? RADIO_TOKENS.radioBackground,
1699
+ radioFocusRing:
1700
+ (radio.radio as { focusRing?: string })?.focusRing ?? RADIO_TOKENS.radioFocusRing,
1701
+ optionBorderActive:
1702
+ (radio as { option?: { border?: { active?: string } } })?.option?.border?.active ??
1703
+ RADIO_TOKENS.optionBorderActive,
1704
+ optionBorderInactive:
1705
+ (radio as { option?: { border?: { inactive?: string } } })?.option?.border?.inactive ??
1706
+ RADIO_TOKENS.optionBorderInactive,
1707
+ optionBackgroundActive:
1708
+ (radio as { option?: { background?: { active?: string } } })?.option?.background?.active ??
1709
+ RADIO_TOKENS.optionBackgroundActive,
1710
+ optionBackgroundInactive:
1711
+ (radio as { option?: { background?: { inactive?: string } } })?.option?.background
1712
+ ?.inactive ?? RADIO_TOKENS.optionBackgroundInactive,
1713
+ optionTextActive:
1714
+ (radio as { option?: { text?: { active?: string } } })?.option?.text?.active ??
1715
+ RADIO_TOKENS.optionTextActive,
1716
+ optionTextInactive:
1717
+ (radio as { option?: { text?: { inactive?: string } } })?.option?.text?.inactive ??
1718
+ RADIO_TOKENS.optionTextInactive,
1719
+ labelMarginBottom: radio.label?.marginBottom ?? RADIO_TOKENS.labelMarginBottom,
1720
+ groupGapHorizontal: radio.group?.gapHorizontal ?? RADIO_TOKENS.groupGapHorizontal,
1721
+ groupGapVertical: radio.group?.gapVertical ?? RADIO_TOKENS.groupGapVertical,
1722
+ descriptionFontSize: radio.description?.fontSize ?? RADIO_TOKENS.descriptionFontSize,
1723
+ descriptionColor: radio.description?.color ?? RADIO_TOKENS.descriptionColor,
1724
+ descriptionFontWeight:
1725
+ typeof radio.description?.fontWeight === 'string' ? parseInt(radio.description.fontWeight)
1726
+ : typeof radio.description?.fontWeight === 'number' ? radio.description.fontWeight
1727
+ : RADIO_TOKENS.descriptionFontWeight,
1728
+ helperFontSize:
1729
+ (radio as { helper?: { fontSize?: string } }).helper?.fontSize ?? RADIO_TOKENS.helperFontSize,
1730
+ helperColor:
1731
+ (radio as { helper?: { color?: string } }).helper?.color ?? RADIO_TOKENS.helperColor,
1732
+ helperErrorColor:
1733
+ (radio as { helper?: { errorColor?: string } }).helper?.errorColor ??
1734
+ RADIO_TOKENS.helperErrorColor,
1735
+ helperMarginTop:
1736
+ (radio as { helper?: { marginTop?: string } }).helper?.marginTop ??
1737
+ RADIO_TOKENS.helperMarginTop,
1738
+ };
1739
+ }
1740
+
1741
+ // Checkbox tokens (legacy format)
1742
+ export interface CheckboxTokens {
1743
+ headingFontSize: string;
1744
+ headingFontWeight: number;
1745
+ headingColor: string;
1746
+ headingMarginBottom: string;
1747
+ textFontSize: string;
1748
+ textFontWeight: number;
1749
+ textColorActive: string;
1750
+ textColorInactive: string;
1751
+ textMarginLeft: string;
1752
+ groupGapHorizontal: string;
1753
+ groupGapVertical: string;
1754
+ checkboxColorActive: string;
1755
+ checkboxColorInactive: string;
1756
+ checkboxBackgroundInactive: string;
1757
+ checkedBorderColor: string;
1758
+ checkboxSizeSm: string;
1759
+ checkboxSizeMd: string;
1760
+ checkboxSizeLg: string;
1761
+ checkboxBorderRadiusSm: string;
1762
+ checkboxBorderRadiusMd: string;
1763
+ checkboxBorderRadiusLg: string;
1764
+ textFontSizeSm: string;
1765
+ textFontSizeMd: string;
1766
+ textFontSizeLg: string;
1767
+ headingFontSizeSm: string;
1768
+ headingFontSizeMd: string;
1769
+ headingFontSizeLg: string;
1770
+ helperTextFontSize: string;
1771
+ helperTextFontWeight: number;
1772
+ helperTextColor: string;
1773
+ helperTextErrorColor: string;
1774
+ helperTextMarginTop: string;
1775
+ }
1776
+
1777
+ const cbSize = components.checkbox.size;
1778
+ export const CHECKBOX_TOKENS: CheckboxTokens = {
1779
+ headingFontSize: components.checkbox.heading.fontSize,
1780
+ headingFontWeight:
1781
+ typeof components.checkbox.heading.fontWeight === 'string' ?
1782
+ parseInt(components.checkbox.heading.fontWeight)
1783
+ : components.checkbox.heading.fontWeight,
1784
+ headingColor: components.checkbox.heading.color,
1785
+ headingMarginBottom: components.checkbox.heading.marginBottom,
1786
+ textFontSize: components.checkbox.label.fontSize,
1787
+ textFontWeight:
1788
+ typeof components.checkbox.label.fontWeight === 'string' ?
1789
+ parseInt(components.checkbox.label.fontWeight)
1790
+ : components.checkbox.label.fontWeight,
1791
+ textColorActive: components.checkbox.label.color.active,
1792
+ textColorInactive: components.checkbox.label.color.inactive,
1793
+ textMarginLeft: components.checkbox.label.marginLeft,
1794
+ groupGapHorizontal: components.checkbox.group.gapHorizontal,
1795
+ groupGapVertical: components.checkbox.group.gapVertical,
1796
+ checkboxColorActive: components.checkbox.checkbox.color.active,
1797
+ checkboxColorInactive: components.checkbox.checkbox.color.inactive,
1798
+ checkboxBackgroundInactive: components.checkbox.checkbox.background.inactive,
1799
+ checkedBorderColor: components.checkbox.checkbox.border.checked,
1800
+ checkboxSizeSm: cbSize.sm.checkboxSize,
1801
+ checkboxSizeMd: cbSize.md.checkboxSize,
1802
+ checkboxSizeLg: cbSize.lg.checkboxSize,
1803
+ checkboxBorderRadiusSm: cbSize.sm.borderRadius,
1804
+ checkboxBorderRadiusMd: cbSize.md.borderRadius,
1805
+ checkboxBorderRadiusLg: cbSize.lg.borderRadius,
1806
+ textFontSizeSm: cbSize.sm.labelFontSize,
1807
+ textFontSizeMd: cbSize.md.labelFontSize,
1808
+ textFontSizeLg: cbSize.lg.labelFontSize,
1809
+ headingFontSizeSm: cbSize.sm.headingFontSize,
1810
+ headingFontSizeMd: cbSize.md.headingFontSize,
1811
+ headingFontSizeLg: cbSize.lg.headingFontSize,
1812
+ helperTextFontSize: components.checkbox.helper.textFontSizes,
1813
+ helperTextFontWeight: components.checkbox.helper.textFontWeight,
1814
+ helperTextColor: components.checkbox.helper.textColor,
1815
+ helperTextErrorColor: components.checkbox.helper.errorColor,
1816
+ helperTextMarginTop: components.checkbox.helper.marginTop,
1817
+ };
1818
+
1819
+ /** Build Checkbox tokens from resolved tokens (e.g. from useTokens()). Use this so tokens update when theme/screen changes. */
1820
+ export function getCheckboxTokensFromResolved(resolved: ResolvedTokens): CheckboxTokens {
1821
+ const cb = resolved.components?.checkbox;
1822
+ if (!cb) return CHECKBOX_TOKENS;
1823
+ const cbSize = cb.size ?? {};
1824
+ const sm = cbSize.sm ?? {};
1825
+ const md = cbSize.md ?? {};
1826
+ const lg = cbSize.lg ?? {};
1827
+ return {
1828
+ headingFontSize: cb.heading?.fontSize ?? CHECKBOX_TOKENS.headingFontSize,
1829
+ headingFontWeight:
1830
+ typeof cb.heading?.fontWeight === 'string' ? parseInt(cb.heading.fontWeight)
1831
+ : typeof cb.heading?.fontWeight === 'number' ? cb.heading.fontWeight
1832
+ : CHECKBOX_TOKENS.headingFontWeight,
1833
+ headingColor: cb.heading?.color ?? CHECKBOX_TOKENS.headingColor,
1834
+ headingMarginBottom: cb.heading?.marginBottom ?? CHECKBOX_TOKENS.headingMarginBottom,
1835
+ textFontSize: cb.label?.fontSize ?? CHECKBOX_TOKENS.textFontSize,
1836
+ textFontWeight:
1837
+ typeof cb.label?.fontWeight === 'string' ? parseInt(cb.label.fontWeight)
1838
+ : typeof cb.label?.fontWeight === 'number' ? cb.label.fontWeight
1839
+ : CHECKBOX_TOKENS.textFontWeight,
1840
+ textColorActive: cb.label?.color?.active ?? CHECKBOX_TOKENS.textColorActive,
1841
+ textColorInactive: cb.label?.color?.inactive ?? CHECKBOX_TOKENS.textColorInactive,
1842
+ textMarginLeft: cb.label?.marginLeft ?? CHECKBOX_TOKENS.textMarginLeft,
1843
+ groupGapHorizontal: cb.group?.gapHorizontal ?? CHECKBOX_TOKENS.groupGapHorizontal,
1844
+ groupGapVertical: cb.group?.gapVertical ?? CHECKBOX_TOKENS.groupGapVertical,
1845
+ checkboxColorActive: cb.checkbox?.color?.active ?? CHECKBOX_TOKENS.checkboxColorActive,
1846
+ checkboxColorInactive: cb.checkbox?.color?.inactive ?? CHECKBOX_TOKENS.checkboxColorInactive,
1847
+ checkboxBackgroundInactive:
1848
+ cb.checkbox?.background?.inactive ?? CHECKBOX_TOKENS.checkboxBackgroundInactive,
1849
+ checkedBorderColor: cb.checkbox?.border?.checked ?? CHECKBOX_TOKENS.checkedBorderColor,
1850
+ checkboxSizeSm: sm.checkboxSize ?? CHECKBOX_TOKENS.checkboxSizeSm,
1851
+ checkboxSizeMd: md.checkboxSize ?? CHECKBOX_TOKENS.checkboxSizeMd,
1852
+ checkboxSizeLg: cb.size.lg.checkboxSize ?? CHECKBOX_TOKENS.checkboxSizeLg,
1853
+ checkboxBorderRadiusSm: sm.borderRadius ?? CHECKBOX_TOKENS.checkboxBorderRadiusSm,
1854
+ checkboxBorderRadiusMd: md.borderRadius ?? CHECKBOX_TOKENS.checkboxBorderRadiusMd,
1855
+ checkboxBorderRadiusLg: lg.borderRadius ?? CHECKBOX_TOKENS.checkboxBorderRadiusLg,
1856
+ textFontSizeSm: sm.labelFontSize ?? CHECKBOX_TOKENS.textFontSizeSm,
1857
+ textFontSizeMd: md.labelFontSize ?? CHECKBOX_TOKENS.textFontSizeMd,
1858
+ textFontSizeLg: lg.labelFontSize ?? CHECKBOX_TOKENS.textFontSizeLg,
1859
+ headingFontSizeSm: sm.headingFontSize ?? CHECKBOX_TOKENS.headingFontSizeSm,
1860
+ headingFontSizeMd: md.headingFontSize ?? CHECKBOX_TOKENS.headingFontSizeMd,
1861
+ headingFontSizeLg: lg.headingFontSize ?? CHECKBOX_TOKENS.headingFontSizeLg,
1862
+ helperTextFontSize: cb.helper?.textFontSize ?? CHECKBOX_TOKENS.helperTextFontSize,
1863
+ helperTextFontWeight: cb.helper?.textFontWeight ?? CHECKBOX_TOKENS.helperTextFontWeight,
1864
+ helperTextColor: cb.helper?.textColor ?? CHECKBOX_TOKENS.helperTextColor,
1865
+ helperTextErrorColor: cb.helper?.errorColor ?? CHECKBOX_TOKENS.helperTextErrorColor,
1866
+ helperTextMarginTop: cb.helper?.marginTop ?? CHECKBOX_TOKENS.helperTextMarginTop,
1867
+ };
1868
+ }
1869
+
1870
+ // Radio tokens (legacy format; from components/radio.json)
1871
+ export interface RadioTokens {
1872
+ headingFontSize: string;
1873
+ headingFontWeight: number;
1874
+ headingFontFamily: string;
1875
+ headingColor: string;
1876
+ headingMarginBottom: string;
1877
+ labelFontSize: string;
1878
+ labelFontWeight: number;
1879
+ labelFontFamily: string;
1880
+ labelColorActive: string;
1881
+ labelColorInactive: string;
1882
+ labelMarginLeft: string;
1883
+ radioSize: string;
1884
+ radioDotSize: string;
1885
+ radioBorderRadius: string;
1886
+ radioStrokeWidth: string;
1887
+ radioColorActive: string;
1888
+ radioColorInactive: string;
1889
+ radioColorDot: string;
1890
+ radioBackground: string;
1891
+ radioFocusRing: string;
1892
+ optionBorderActive: string;
1893
+ optionBorderInactive: string;
1894
+ optionBackgroundActive: string;
1895
+ optionBackgroundInactive: string;
1896
+ optionTextActive: string;
1897
+ optionTextInactive: string;
1898
+ labelMarginBottom: string;
1899
+ groupGapHorizontal: string;
1900
+ groupGapVertical: string;
1901
+ descriptionFontSize: string;
1902
+ descriptionColor: string;
1903
+ descriptionFontWeight: number;
1904
+ helperFontSize: string;
1905
+ helperColor: string;
1906
+ helperErrorColor: string;
1907
+ helperMarginTop: string;
1908
+ }
1909
+
1910
+ export const RADIO_TOKENS: RadioTokens = {
1911
+ headingFontSize: components.radio.heading.fontSize,
1912
+ headingFontWeight:
1913
+ typeof components.radio.heading.fontWeight === 'string' ?
1914
+ parseInt(components.radio.heading.fontWeight)
1915
+ : components.radio.heading.fontWeight,
1916
+ headingFontFamily: components.radio.heading.fontFamily,
1917
+ headingColor: components.radio.heading.color,
1918
+ headingMarginBottom: components.radio.heading.marginBottom,
1919
+ labelFontSize: components.radio.label.fontSize,
1920
+ labelFontWeight:
1921
+ typeof components.radio.label.fontWeight === 'string' ?
1922
+ parseInt(components.radio.label.fontWeight)
1923
+ : components.radio.label.fontWeight,
1924
+ labelFontFamily: components.radio.label.fontFamily,
1925
+ labelColorActive: components.radio.label.color.active,
1926
+ labelColorInactive: components.radio.label.color.inactive,
1927
+ labelMarginLeft: components.radio.label.marginLeft,
1928
+ radioSize: components.radio.radio.size,
1929
+ radioDotSize:
1930
+ (components.radio.size?.md as { dotSize?: string })?.dotSize ??
1931
+ (components.radio.size?.sm as { dotSize?: string })?.dotSize ??
1932
+ foundation.size['8'],
1933
+ radioBorderRadius: components.radio.radio.borderRadius,
1934
+ radioStrokeWidth: components.radio.radio.strokeWidth,
1935
+ radioColorActive: components.radio.radio.color.active,
1936
+ radioColorInactive: components.radio.radio.color.inactive,
1937
+ radioColorDot:
1938
+ (components.radio.radio as { color?: { active?: string; dot?: string } }).color?.dot ??
1939
+ (components.radio.radio as { color?: { active?: string } }).color?.active ??
1940
+ components.radio.radio.color.active,
1941
+ radioBackground: components.radio.radio.background,
1942
+ radioFocusRing: (components.radio.radio as { focusRing?: string }).focusRing ?? 'none',
1943
+ optionBorderActive:
1944
+ (components.radio as { option?: { border?: { active?: string } } }).option?.border?.active ??
1945
+ components.radio.radio.color.active,
1946
+ optionBorderInactive:
1947
+ (components.radio as { option?: { border?: { inactive?: string } } }).option?.border
1948
+ ?.inactive ?? components.radio.radio.color.inactive,
1949
+ optionBackgroundActive:
1950
+ (components.radio as { option?: { background?: { active?: string } } }).option?.background
1951
+ ?.active ?? components.radio.radio.background,
1952
+ optionBackgroundInactive:
1953
+ (components.radio as { option?: { background?: { inactive?: string } } }).option?.background
1954
+ ?.inactive ?? components.radio.radio.background,
1955
+ optionTextActive:
1956
+ (components.radio as { option?: { text?: { active?: string } } }).option?.text?.active ??
1957
+ components.radio.label.color.active,
1958
+ optionTextInactive:
1959
+ (components.radio as { option?: { text?: { inactive?: string } } }).option?.text?.inactive ??
1960
+ components.radio.label.color.inactive,
1961
+ labelMarginBottom: components.radio.label.marginBottom,
1962
+ groupGapHorizontal: components.radio.group.gapHorizontal,
1963
+ groupGapVertical: components.radio.group.gapVertical,
1964
+ descriptionFontSize: components.radio.description.fontSize,
1965
+ descriptionColor: components.radio.description.color,
1966
+ descriptionFontWeight:
1967
+ typeof components.radio.description.fontWeight === 'string' ?
1968
+ parseInt(components.radio.description.fontWeight)
1969
+ : components.radio.description.fontWeight,
1970
+ helperFontSize:
1971
+ (components.radio as { helper?: { fontSize?: string } }).helper?.fontSize ??
1972
+ components.radio.description.fontSize,
1973
+ helperColor:
1974
+ (components.radio as { helper?: { color?: string } }).helper?.color ??
1975
+ components.radio.description.color,
1976
+ helperErrorColor:
1977
+ (components.radio as { helper?: { errorColor?: string } }).helper?.errorColor ?? '#c00',
1978
+ helperMarginTop:
1979
+ (components.radio as { helper?: { marginTop?: string } }).helper?.marginTop ?? '4px',
1980
+ };
1981
+
1982
+ // SelectScroll tokens (legacy format; from components/selectscroll.json)
1983
+ export interface SelectScrollTokens {
1984
+ // Input
1985
+ inputHeight: string;
1986
+ inputHeightSm: string;
1987
+ inputHeightMd: string;
1988
+ inputHeightLg: string;
1989
+ inputPadding: string;
1990
+ inputPaddingSm: string;
1991
+ inputPaddingMd: string;
1992
+ inputPaddingLg: string;
1993
+ inputBorderDefault: string;
1994
+ inputBorderActive: string;
1995
+ inputBorderError: string;
1996
+ inputBorderRadius: string;
1997
+ inputFontSize: string;
1998
+ inputFontSizeSm: string;
1999
+ inputFontSizeMd: string;
2000
+ inputFontSizeLg: string;
2001
+ inputColor: string;
2002
+ inputBackground: string;
2003
+ inputBackgroundWhite: string;
2004
+ inputBackgroundDisabled: string;
2005
+ inputDisabledOpacity: string;
2006
+ // Listbox
2007
+ listboxBackground: string;
2008
+ listboxBorder: string;
2009
+ listboxBorderRadius: string;
2010
+ // Option
2011
+ optionPadding: string;
2012
+ optionFontSize: string;
2013
+ optionColor: string;
2014
+ optionHoverBackground: string;
2015
+ // Label
2016
+ labelFontSize: string;
2017
+ labelFontWeight: number;
2018
+ labelMarginBottom: string;
2019
+ labelColor: string;
2020
+ labelRequiredColor: string;
2021
+ // Helper
2022
+ helperFontSize: string;
2023
+ helperColor: string;
2024
+ helperErrorColor: string;
2025
+ // Placeholder
2026
+ placeholderColor: string;
2027
+ // Button
2028
+ buttonHoverBackground: string;
2029
+ buttonHoverColor: string;
2030
+ buttonColor: string;
2031
+ }
2032
+
2033
+ const ssInputSize = components.selectscroll.input.size;
2034
+ export const SELECT_SCROLL_TOKENS: SelectScrollTokens = {
2035
+ // Input
2036
+ inputHeight: ssInputSize.md.height,
2037
+ inputHeightSm: ssInputSize.sm.height,
2038
+ inputHeightMd: ssInputSize.md.height,
2039
+ inputHeightLg: ssInputSize.lg.height,
2040
+ inputPadding: ssInputSize.md.padding,
2041
+ inputPaddingSm: ssInputSize.sm.padding,
2042
+ inputPaddingMd: ssInputSize.md.padding,
2043
+ inputPaddingLg: ssInputSize.lg.padding,
2044
+ inputBorderDefault: components.selectscroll.input.border.default,
2045
+ inputBorderActive: components.selectscroll.input.border.active,
2046
+ inputBorderError: components.selectscroll.input.border.error,
2047
+ inputBorderRadius: ssInputSize.md.borderRadius,
2048
+ inputFontSize: ssInputSize.md.fontSize,
2049
+ inputFontSizeSm: ssInputSize.sm.fontSize,
2050
+ inputFontSizeMd: ssInputSize.md.fontSize,
2051
+ inputFontSizeLg: ssInputSize.lg.fontSize,
2052
+ inputColor: components.selectscroll.input.color,
2053
+ inputBackground: components.selectscroll.input.background,
2054
+ inputBackgroundWhite:
2055
+ components.selectscroll.input.backgroundWhite ?? components.selectscroll.input.background,
2056
+ inputBackgroundDisabled: components.selectscroll.input.backgroundDisabled,
2057
+ inputDisabledOpacity: components.selectscroll.input.disabledOpacity,
2058
+ // Listbox
2059
+ listboxBackground: components.selectscroll.listbox.background,
2060
+ listboxBorder: components.selectscroll.listbox.border,
2061
+ listboxBorderRadius: components.selectscroll.listbox.borderRadius,
2062
+ // Option
2063
+ optionPadding: components.selectscroll.option.padding,
2064
+ optionFontSize: components.selectscroll.option.fontSize,
2065
+ optionColor: components.selectscroll.option.color,
2066
+ optionHoverBackground: components.selectscroll.option.hoverBackground,
2067
+ // Label
2068
+ labelFontSize: components.selectscroll.label.fontSize,
2069
+ labelFontWeight:
2070
+ typeof components.selectscroll.label.fontWeight === 'string' ?
2071
+ parseInt(components.selectscroll.label.fontWeight)
2072
+ : components.selectscroll.label.fontWeight,
2073
+ labelMarginBottom: components.selectscroll.label.marginBottom,
2074
+ labelColor: components.selectscroll.label.color,
2075
+ labelRequiredColor: components.selectscroll.label.requiredColor,
2076
+ // Helper
2077
+ helperFontSize: components.selectscroll.helper.fontSize,
2078
+ helperColor: components.selectscroll.helper.color,
2079
+ helperErrorColor: components.selectscroll.helper.errorColor,
2080
+ // Placeholder
2081
+ placeholderColor: components.selectscroll.placeholderColor,
2082
+ // Button
2083
+ buttonHoverBackground: components.selectscroll.button.hoverBackground,
2084
+ buttonHoverColor: components.selectscroll.button.hoverColor,
2085
+ buttonColor: components.selectscroll.button.color,
2086
+ };
2087
+
2088
+ /** Build SelectScroll tokens from resolved tokens (e.g. from useTokens()). Use this so tokens update when theme mode (light/dark) or brand changes. */
2089
+ export function getSelectScrollTokensFromResolved(resolved: ResolvedTokens): SelectScrollTokens {
2090
+ const ss = resolved.components?.selectscroll as Record<string, any> | undefined;
2091
+ if (!ss?.input?.size) return SELECT_SCROLL_TOKENS;
2092
+ const inp = ss.input;
2093
+ const sm = inp.size?.sm;
2094
+ const md = inp.size?.md;
2095
+ const lg = inp.size?.lg;
2096
+ return {
2097
+ inputHeight: md?.height ?? SELECT_SCROLL_TOKENS.inputHeightMd,
2098
+ inputHeightSm: sm?.height ?? SELECT_SCROLL_TOKENS.inputHeightSm,
2099
+ inputHeightMd: md?.height ?? SELECT_SCROLL_TOKENS.inputHeightMd,
2100
+ inputHeightLg: lg?.height ?? SELECT_SCROLL_TOKENS.inputHeightLg,
2101
+ inputPadding: md?.padding ?? SELECT_SCROLL_TOKENS.inputPaddingMd,
2102
+ inputPaddingSm: sm?.padding ?? SELECT_SCROLL_TOKENS.inputPaddingSm,
2103
+ inputPaddingMd: md?.padding ?? SELECT_SCROLL_TOKENS.inputPaddingMd,
2104
+ inputPaddingLg: lg?.padding ?? SELECT_SCROLL_TOKENS.inputPaddingLg,
2105
+ inputBorderDefault: inp.border?.default ?? SELECT_SCROLL_TOKENS.inputBorderDefault,
2106
+ inputBorderActive: inp.border?.active ?? SELECT_SCROLL_TOKENS.inputBorderActive,
2107
+ inputBorderError: inp.border?.error ?? SELECT_SCROLL_TOKENS.inputBorderError,
2108
+ inputBorderRadius: md?.borderRadius ?? SELECT_SCROLL_TOKENS.inputBorderRadius,
2109
+ inputFontSize: md?.fontSize ?? SELECT_SCROLL_TOKENS.inputFontSizeMd,
2110
+ inputFontSizeSm: sm?.fontSize ?? SELECT_SCROLL_TOKENS.inputFontSizeSm,
2111
+ inputFontSizeMd: md?.fontSize ?? SELECT_SCROLL_TOKENS.inputFontSizeMd,
2112
+ inputFontSizeLg: lg?.fontSize ?? SELECT_SCROLL_TOKENS.inputFontSizeLg,
2113
+ inputColor: inp.color ?? SELECT_SCROLL_TOKENS.inputColor,
2114
+ inputBackground: inp.background ?? SELECT_SCROLL_TOKENS.inputBackground,
2115
+ inputBackgroundWhite:
2116
+ inp.backgroundWhite ?? inp.background ?? SELECT_SCROLL_TOKENS.inputBackgroundWhite,
2117
+ inputBackgroundDisabled: inp.backgroundDisabled ?? SELECT_SCROLL_TOKENS.inputBackgroundDisabled,
2118
+ inputDisabledOpacity: inp.disabledOpacity ?? SELECT_SCROLL_TOKENS.inputDisabledOpacity,
2119
+ listboxBackground: ss.listbox?.background ?? SELECT_SCROLL_TOKENS.listboxBackground,
2120
+ listboxBorder: ss.listbox?.border ?? SELECT_SCROLL_TOKENS.listboxBorder,
2121
+ listboxBorderRadius: ss.listbox?.borderRadius ?? SELECT_SCROLL_TOKENS.listboxBorderRadius,
2122
+ optionPadding: ss.option?.padding ?? SELECT_SCROLL_TOKENS.optionPadding,
2123
+ optionFontSize: ss.option?.fontSize ?? SELECT_SCROLL_TOKENS.optionFontSize,
2124
+ optionColor: ss.option?.color ?? SELECT_SCROLL_TOKENS.optionColor,
2125
+ optionHoverBackground: ss.option?.hoverBackground ?? SELECT_SCROLL_TOKENS.optionHoverBackground,
2126
+ labelFontSize: ss.label?.fontSize ?? SELECT_SCROLL_TOKENS.labelFontSize,
2127
+ labelFontWeight:
2128
+ typeof ss.label?.fontWeight === 'string' ?
2129
+ parseInt(ss.label.fontWeight)
2130
+ : (ss.label?.fontWeight ?? SELECT_SCROLL_TOKENS.labelFontWeight),
2131
+ labelMarginBottom: ss.label?.marginBottom ?? SELECT_SCROLL_TOKENS.labelMarginBottom,
2132
+ labelColor: ss.label?.color ?? SELECT_SCROLL_TOKENS.labelColor,
2133
+ labelRequiredColor: ss.label?.requiredColor ?? SELECT_SCROLL_TOKENS.labelRequiredColor,
2134
+ helperFontSize: ss.helper?.fontSize ?? SELECT_SCROLL_TOKENS.helperFontSize,
2135
+ helperColor: ss.helper?.color ?? SELECT_SCROLL_TOKENS.helperColor,
2136
+ helperErrorColor: ss.helper?.errorColor ?? SELECT_SCROLL_TOKENS.helperErrorColor,
2137
+ placeholderColor: ss.placeholderColor ?? SELECT_SCROLL_TOKENS.placeholderColor,
2138
+ buttonHoverBackground: ss.button?.hoverBackground ?? SELECT_SCROLL_TOKENS.buttonHoverBackground,
2139
+ buttonHoverColor: ss.button?.hoverColor ?? SELECT_SCROLL_TOKENS.buttonHoverColor,
2140
+ buttonColor: ss.button?.color ?? SELECT_SCROLL_TOKENS.buttonColor,
2141
+ };
2142
+ }
2143
+
2144
+ // Selector tokens (legacy format; from components/selector.json)
2145
+ export interface SelectorTokens {
2146
+ // Input
2147
+ inputHeight: string;
2148
+ inputHeightXs: string;
2149
+ inputHeightSm: string;
2150
+ inputHeightMd: string;
2151
+ inputHeightLg: string;
2152
+ inputPadding: string;
2153
+ inputPaddingXs: string;
2154
+ inputPaddingSm: string;
2155
+ inputPaddingMd: string;
2156
+ inputPaddingLg: string;
2157
+ inputBorderDefault: string;
2158
+ inputBorderActive: string;
2159
+ inputBorderError: string;
2160
+ inputBorderRadius: string;
2161
+ inputBorderRadiusXs: string;
2162
+ inputBorderRadiusSm: string;
2163
+ inputBorderRadiusMd: string;
2164
+ inputBorderRadiusLg: string;
2165
+ inputFontSize: string;
2166
+ inputFontSizeXs: string;
2167
+ inputFontSizeSm: string;
2168
+ inputFontSizeMd: string;
2169
+ inputFontSizeLg: string;
2170
+ inputColor: string;
2171
+ inputBackground: string;
2172
+ inputBackgroundWhite: string;
2173
+ inputBackgroundDisabled: string;
2174
+ inputDisabledOpacity: string;
2175
+ // Wrapper
2176
+ wrapperBorderRadius: string;
2177
+ wrapperBackground: string;
2178
+ wrapperBackgroundWhite: string;
2179
+ wrapperBackgroundDisabled: string;
2180
+ // Chip
2181
+ chipFontSize: string;
2182
+ chipBackground: string;
2183
+ chipColor: string;
2184
+ chipBorderRadius: string;
2185
+ // Button
2186
+ buttonHoverBackground: string;
2187
+ buttonHoverColor: string;
2188
+ buttonColor: string;
2189
+ // Label
2190
+ labelFontSize: string;
2191
+ labelFontWeight: number;
2192
+ labelMarginBottom: string;
2193
+ labelColor: string;
2194
+ labelRequiredColor: string;
2195
+ // Helper
2196
+ helperFontSize: string;
2197
+ helperColor: string;
2198
+ helperErrorColor: string;
2199
+ // Option
2200
+ optionPadding: string;
2201
+ optionFontSize: string;
2202
+ optionColor: string;
2203
+ optionHoverBackground: string;
2204
+ // Listbox
2205
+ listboxBackground: string;
2206
+ listboxGroupHeadingBackground: string;
2207
+ listboxGroupHeadingBorder: string;
2208
+ listboxBorder: string;
2209
+ listboxBorderRadius: string;
2210
+ // Checkbox
2211
+ checkboxAccentColor: string;
2212
+ }
2213
+
2214
+ const selInputSize = components.selector.input.size;
2215
+ export const SELECTOR_TOKENS: SelectorTokens = {
2216
+ // Input
2217
+ inputHeight: selInputSize.md.height,
2218
+ inputHeightXs: selInputSize.xs.height,
2219
+ inputHeightSm: selInputSize.sm.height,
2220
+ inputHeightMd: selInputSize.md.height,
2221
+ inputHeightLg: selInputSize.lg.height,
2222
+ inputPadding: selInputSize.md.padding,
2223
+ inputPaddingXs: selInputSize.xs.padding,
2224
+ inputPaddingSm: selInputSize.sm.padding,
2225
+ inputPaddingMd: selInputSize.md.padding,
2226
+ inputPaddingLg: selInputSize.lg.padding,
2227
+ inputBorderDefault: components.selector.input.border.default,
2228
+ inputBorderActive: components.selector.input.border.active,
2229
+ inputBorderError: components.selector.input.border.error,
2230
+ inputBorderRadius: selInputSize.md.borderRadius,
2231
+ inputBorderRadiusXs: selInputSize.xs.borderRadius,
2232
+ inputBorderRadiusSm: selInputSize.sm.borderRadius,
2233
+ inputBorderRadiusMd: selInputSize.md.borderRadius,
2234
+ inputBorderRadiusLg: selInputSize.lg.borderRadius,
2235
+ inputFontSize: selInputSize.md.fontSize,
2236
+ inputFontSizeXs: selInputSize.xs.fontSize,
2237
+ inputFontSizeSm: selInputSize.sm.fontSize,
2238
+ inputFontSizeMd: selInputSize.md.fontSize,
2239
+ inputFontSizeLg: selInputSize.lg.fontSize,
2240
+ inputColor: components.selector.input.color,
2241
+ inputBackground: components.selector.input.background,
2242
+ inputBackgroundWhite:
2243
+ components.selector.input.backgroundWhite ?? components.selector.input.background,
2244
+ inputBackgroundDisabled: components.selector.input.backgroundDisabled,
2245
+ inputDisabledOpacity: components.selector.input.disabledOpacity,
2246
+ // Wrapper
2247
+ wrapperBorderRadius: components.selector.wrapper.borderRadius,
2248
+ wrapperBackground: components.selector.wrapper.background,
2249
+ wrapperBackgroundWhite:
2250
+ components.selector.wrapper.backgroundWhite ?? components.selector.wrapper.background,
2251
+ wrapperBackgroundDisabled: components.selector.wrapper.backgroundDisabled,
2252
+ // Chip
2253
+ chipFontSize: components.selector.chip.fontSize,
2254
+ chipBackground: components.selector.chip.background,
2255
+ chipColor: components.selector.chip.color,
2256
+ chipBorderRadius: components.selector.chip.borderRadius,
2257
+ // Button
2258
+ buttonHoverBackground: components.selector.button.hoverBackground,
2259
+ buttonHoverColor: components.selector.button.hoverColor,
2260
+ buttonColor: components.selector.button.color,
2261
+ // Label
2262
+ labelFontSize: components.selector.label.fontSize,
2263
+ labelFontWeight:
2264
+ typeof components.selector.label.fontWeight === 'string' ?
2265
+ parseInt(components.selector.label.fontWeight)
2266
+ : components.selector.label.fontWeight,
2267
+ labelMarginBottom: components.selector.label.marginBottom,
2268
+ labelColor: components.selector.label.color,
2269
+ labelRequiredColor: components.selector.label.requiredColor,
2270
+ // Helper
2271
+ helperFontSize: components.selector.helper.fontSize,
2272
+ helperColor: components.selector.helper.color,
2273
+ helperErrorColor: components.selector.helper.errorColor,
2274
+ // Option
2275
+ optionPadding: components.selector.option.padding,
2276
+ optionFontSize: components.selector.option.fontSize,
2277
+ optionColor: components.selector.option.color,
2278
+ optionHoverBackground: components.selector.option.hoverBackground,
2279
+ // Listbox
2280
+ listboxBackground: components.selector.listbox.background,
2281
+ listboxGroupHeadingBackground:
2282
+ components.selector.listbox.groupHeadingBackground ?? components.selector.listbox.background,
2283
+ listboxGroupHeadingBorder:
2284
+ components.selector.listbox.groupHeadingBorder ?? '1px solid rgba(0,0,0,0.06)',
2285
+ listboxBorder: components.selector.listbox.border,
2286
+ listboxBorderRadius: components.selector.listbox.borderRadius,
2287
+ // Checkbox
2288
+ checkboxAccentColor: components.selector.checkbox.accentColor,
2289
+ };
2290
+
2291
+ /** Build Selector tokens from resolved tokens (e.g. from useTokens()). Use this so tokens update when theme mode (light/dark) or brand changes. */
2292
+ export function getSelectorTokensFromResolved(resolved: ResolvedTokens): SelectorTokens {
2293
+ const sel = resolved.components?.selector as Record<string, any> | undefined;
2294
+ if (!sel?.input?.size) return SELECTOR_TOKENS;
2295
+ const inp = sel.input;
2296
+ const xs = inp.size?.xs;
2297
+ const sm = inp.size?.sm;
2298
+ const md = inp.size?.md;
2299
+ const lg = inp.size?.lg;
2300
+ return {
2301
+ inputHeight: md?.height ?? SELECTOR_TOKENS.inputHeightMd,
2302
+ inputHeightXs: xs?.height ?? SELECTOR_TOKENS.inputHeightXs,
2303
+ inputHeightSm: sm?.height ?? SELECTOR_TOKENS.inputHeightSm,
2304
+ inputHeightMd: md?.height ?? SELECTOR_TOKENS.inputHeightMd,
2305
+ inputHeightLg: lg?.height ?? SELECTOR_TOKENS.inputHeightLg,
2306
+ inputPadding: md?.padding ?? SELECTOR_TOKENS.inputPaddingMd,
2307
+ inputPaddingXs: xs?.padding ?? SELECTOR_TOKENS.inputPaddingXs,
2308
+ inputPaddingSm: sm?.padding ?? SELECTOR_TOKENS.inputPaddingSm,
2309
+ inputPaddingMd: md?.padding ?? SELECTOR_TOKENS.inputPaddingMd,
2310
+ inputPaddingLg: lg?.padding ?? SELECTOR_TOKENS.inputPaddingLg,
2311
+ inputBorderDefault: inp.border?.default ?? SELECTOR_TOKENS.inputBorderDefault,
2312
+ inputBorderActive: inp.border?.active ?? SELECTOR_TOKENS.inputBorderActive,
2313
+ inputBorderError: inp.border?.error ?? SELECTOR_TOKENS.inputBorderError,
2314
+ inputBorderRadius: md?.borderRadius ?? SELECTOR_TOKENS.inputBorderRadiusMd,
2315
+ inputBorderRadiusXs: xs?.borderRadius ?? SELECTOR_TOKENS.inputBorderRadiusXs,
2316
+ inputBorderRadiusSm: sm?.borderRadius ?? SELECTOR_TOKENS.inputBorderRadiusSm,
2317
+ inputBorderRadiusMd: md?.borderRadius ?? SELECTOR_TOKENS.inputBorderRadiusMd,
2318
+ inputBorderRadiusLg: lg?.borderRadius ?? SELECTOR_TOKENS.inputBorderRadiusLg,
2319
+ inputFontSize: md?.fontSize ?? SELECTOR_TOKENS.inputFontSizeMd,
2320
+ inputFontSizeXs: xs?.fontSize ?? SELECTOR_TOKENS.inputFontSizeXs,
2321
+ inputFontSizeSm: sm?.fontSize ?? SELECTOR_TOKENS.inputFontSizeSm,
2322
+ inputFontSizeMd: md?.fontSize ?? SELECTOR_TOKENS.inputFontSizeMd,
2323
+ inputFontSizeLg: lg?.fontSize ?? SELECTOR_TOKENS.inputFontSizeLg,
2324
+ inputColor: inp.color ?? SELECTOR_TOKENS.inputColor,
2325
+ inputBackground: inp.background ?? SELECTOR_TOKENS.inputBackground,
2326
+ inputBackgroundWhite:
2327
+ inp.backgroundWhite ?? inp.background ?? SELECTOR_TOKENS.inputBackgroundWhite,
2328
+ inputBackgroundDisabled: inp.backgroundDisabled ?? SELECTOR_TOKENS.inputBackgroundDisabled,
2329
+ inputDisabledOpacity: inp.disabledOpacity ?? SELECTOR_TOKENS.inputDisabledOpacity,
2330
+ wrapperBorderRadius: sel.wrapper?.borderRadius ?? SELECTOR_TOKENS.wrapperBorderRadius,
2331
+ wrapperBackground: sel.wrapper?.background ?? SELECTOR_TOKENS.wrapperBackground,
2332
+ wrapperBackgroundWhite:
2333
+ sel.wrapper?.backgroundWhite ??
2334
+ sel.wrapper?.background ??
2335
+ SELECTOR_TOKENS.wrapperBackgroundWhite,
2336
+ wrapperBackgroundDisabled:
2337
+ sel.wrapper?.backgroundDisabled ?? SELECTOR_TOKENS.wrapperBackgroundDisabled,
2338
+ chipFontSize: sel.chip?.fontSize ?? SELECTOR_TOKENS.chipFontSize,
2339
+ chipBackground: sel.chip?.background ?? SELECTOR_TOKENS.chipBackground,
2340
+ chipColor: sel.chip?.color ?? SELECTOR_TOKENS.chipColor,
2341
+ chipBorderRadius: sel.chip?.borderRadius ?? SELECTOR_TOKENS.chipBorderRadius,
2342
+ buttonHoverBackground: sel.button?.hoverBackground ?? SELECTOR_TOKENS.buttonHoverBackground,
2343
+ buttonHoverColor: sel.button?.hoverColor ?? SELECTOR_TOKENS.buttonHoverColor,
2344
+ buttonColor: sel.button?.color ?? SELECTOR_TOKENS.buttonColor,
2345
+ labelFontSize: sel.label?.fontSize ?? SELECTOR_TOKENS.labelFontSize,
2346
+ labelFontWeight:
2347
+ typeof sel.label?.fontWeight === 'string' ?
2348
+ parseInt(sel.label.fontWeight)
2349
+ : (sel.label?.fontWeight ?? SELECTOR_TOKENS.labelFontWeight),
2350
+ labelMarginBottom: sel.label?.marginBottom ?? SELECTOR_TOKENS.labelMarginBottom,
2351
+ labelColor: sel.label?.color ?? SELECTOR_TOKENS.labelColor,
2352
+ labelRequiredColor: sel.label?.requiredColor ?? SELECTOR_TOKENS.labelRequiredColor,
2353
+ helperFontSize: sel.helper?.fontSize ?? SELECTOR_TOKENS.helperFontSize,
2354
+ helperColor: sel.helper?.color ?? SELECTOR_TOKENS.helperColor,
2355
+ helperErrorColor: sel.helper?.errorColor ?? SELECTOR_TOKENS.helperErrorColor,
2356
+ optionPadding: sel.option?.padding ?? SELECTOR_TOKENS.optionPadding,
2357
+ optionFontSize: sel.option?.fontSize ?? SELECTOR_TOKENS.optionFontSize,
2358
+ optionColor: sel.option?.color ?? SELECTOR_TOKENS.optionColor,
2359
+ optionHoverBackground: sel.option?.hoverBackground ?? SELECTOR_TOKENS.optionHoverBackground,
2360
+ listboxBackground: sel.listbox?.background ?? SELECTOR_TOKENS.listboxBackground,
2361
+ listboxGroupHeadingBackground:
2362
+ sel.listbox?.groupHeadingBackground ?? SELECTOR_TOKENS.listboxGroupHeadingBackground,
2363
+ listboxGroupHeadingBorder:
2364
+ sel.listbox?.groupHeadingBorder ?? SELECTOR_TOKENS.listboxGroupHeadingBorder,
2365
+ listboxBorder: sel.listbox?.border ?? SELECTOR_TOKENS.listboxBorder,
2366
+ listboxBorderRadius: sel.listbox?.borderRadius ?? SELECTOR_TOKENS.listboxBorderRadius,
2367
+ checkboxAccentColor: sel.checkbox?.accentColor ?? SELECTOR_TOKENS.checkboxAccentColor,
2368
+ };
2369
+ }
2370
+
2371
+ // Pagination tokens (from components/pagination.json)
2372
+ export type PaginationSize = 'xs' | 'sm' | 'md' | 'lg';
2373
+
2374
+ export interface PaginationSizeToken {
2375
+ height: string;
2376
+ minWidth: string;
2377
+ padding: string;
2378
+ fontSize: string;
2379
+ borderRadius: string;
2380
+ }
2381
+
2382
+ export interface PaginationTokens {
2383
+ sizeXs: PaginationSizeToken;
2384
+ sizeSm: PaginationSizeToken;
2385
+ sizeMd: PaginationSizeToken;
2386
+ sizeLg: PaginationSizeToken;
2387
+ activeBackground: string;
2388
+ activeColor: string;
2389
+ inactiveColor: string;
2390
+ iconColor: string;
2391
+ hoverBackground: string;
2392
+ disabledOpacity: string;
2393
+ gap: string;
2394
+ fontFamily: string;
2395
+ fontWeight: number;
2396
+ }
2397
+
2398
+ const pag = components.pagination;
2399
+ const pagSize = pag.size;
2400
+ export const PAGINATION_TOKENS: PaginationTokens = {
2401
+ sizeXs: pagSize.xs,
2402
+ sizeSm: pagSize.sm,
2403
+ sizeMd: pagSize.md,
2404
+ sizeLg: pagSize.lg,
2405
+ activeBackground: pag.item.activeBackground,
2406
+ activeColor: pag.item.activeColor,
2407
+ inactiveColor: pag.item.inactiveColor,
2408
+ iconColor: pag.item.iconColor,
2409
+ hoverBackground: pag.item.hoverBackground,
2410
+ disabledOpacity: pag.item.disabledOpacity,
2411
+ gap: pag.gap,
2412
+ fontFamily: pag.fontFamily,
2413
+ fontWeight:
2414
+ typeof pag.fontWeight === 'string' ? parseInt(pag.fontWeight) : Number(pag.fontWeight ?? 500),
2415
+ };
2416
+
2417
+ /** Build Pagination tokens from resolved tokens (e.g. from useTokens()). Use this so tokens update when theme mode (light/dark) or brand changes. */
2418
+ export function getPaginationTokensFromResolved(resolved: ResolvedTokens): PaginationTokens {
2419
+ const pagResolved = resolved.components?.pagination as Record<string, any> | undefined;
2420
+ if (!pagResolved?.size) return PAGINATION_TOKENS;
2421
+ const size = pagResolved.size;
2422
+ const item = pagResolved.item ?? {};
2423
+ return {
2424
+ sizeXs: size.xs ?? PAGINATION_TOKENS.sizeXs,
2425
+ sizeSm: size.sm ?? PAGINATION_TOKENS.sizeSm,
2426
+ sizeMd: size.md ?? PAGINATION_TOKENS.sizeMd,
2427
+ sizeLg: size.lg ?? PAGINATION_TOKENS.sizeLg,
2428
+ activeBackground: item.activeBackground ?? PAGINATION_TOKENS.activeBackground,
2429
+ activeColor: item.activeColor ?? PAGINATION_TOKENS.activeColor,
2430
+ inactiveColor: item.inactiveColor ?? PAGINATION_TOKENS.inactiveColor,
2431
+ iconColor: item.iconColor ?? PAGINATION_TOKENS.iconColor,
2432
+ hoverBackground: item.hoverBackground ?? PAGINATION_TOKENS.hoverBackground,
2433
+ disabledOpacity: item.disabledOpacity ?? PAGINATION_TOKENS.disabledOpacity,
2434
+ gap: pagResolved.gap ?? PAGINATION_TOKENS.gap,
2435
+ fontFamily: pagResolved.fontFamily ?? PAGINATION_TOKENS.fontFamily,
2436
+ fontWeight:
2437
+ typeof pagResolved.fontWeight === 'string' ?
2438
+ parseInt(pagResolved.fontWeight)
2439
+ : (pagResolved.fontWeight ?? PAGINATION_TOKENS.fontWeight),
2440
+ };
2441
+ }
2442
+
2443
+ // DatePicker tokens (resolved from components/datepicker.json → semantic)
2444
+ export const DATE_PICKER_TOKENS = components.datepicker;
2445
+
2446
+ /** Build DatePicker tokens from resolved tokens (e.g. from useTokens()). Use this so tokens update when theme mode (light/dark) or brand changes. */
2447
+ export function getDatePickerTokensFromResolved(
2448
+ resolved: ResolvedTokens
2449
+ ): typeof DATE_PICKER_TOKENS {
2450
+ const dp = resolved.components?.datepicker as Record<string, any> | undefined;
2451
+ if (!dp) return DATE_PICKER_TOKENS;
2452
+ return {
2453
+ ...DATE_PICKER_TOKENS,
2454
+ ...dp,
2455
+ wrapper: { ...DATE_PICKER_TOKENS.wrapper, ...dp.wrapper },
2456
+ input: { ...DATE_PICKER_TOKENS.input, ...dp.input },
2457
+ header: { ...DATE_PICKER_TOKENS.header, ...dp.header },
2458
+ headerButton: { ...DATE_PICKER_TOKENS.headerButton, ...dp.headerButton },
2459
+ headerSelect: { ...DATE_PICKER_TOKENS.headerSelect, ...dp.headerSelect },
2460
+ label: { ...DATE_PICKER_TOKENS.label, ...dp.label },
2461
+ helper: { ...DATE_PICKER_TOKENS.helper, ...dp.helper },
2462
+ calendar: { ...DATE_PICKER_TOKENS.calendar, ...dp.calendar },
2463
+ size: dp.size ? { ...DATE_PICKER_TOKENS.size, ...dp.size } : DATE_PICKER_TOKENS.size,
2464
+ } as typeof DATE_PICKER_TOKENS;
2465
+ }
2466
+
2467
+ // FileUpload tokens (from components/fileupload.json)
2468
+ export interface FileUploadTokens {
2469
+ containerHeight: string;
2470
+ containerBorderRadius: string;
2471
+ containerBackground: string;
2472
+ containerBackgroundWhite: string;
2473
+ containerBorder: string;
2474
+ containerBorderFocus: string;
2475
+ containerBorderError: string;
2476
+ containerPaddingY: string;
2477
+ containerPaddingX: string;
2478
+ containerGap: string;
2479
+ labelFontSize: string;
2480
+ labelFontWeight: number;
2481
+ labelFontFamily: string;
2482
+ labelColor: string;
2483
+ labelRequiredColor: string;
2484
+ labelMarginBottom: string;
2485
+ buttonFontSize: string;
2486
+ buttonFontWeight: number;
2487
+ buttonFontFamily: string;
2488
+ buttonColor: string;
2489
+ buttonBackground: string;
2490
+ buttonBorder: string;
2491
+ buttonBorderRadius: string;
2492
+ buttonHeight: string;
2493
+ buttonPaddingY: string;
2494
+ buttonPaddingX: string;
2495
+ placeholderFontSize: string;
2496
+ placeholderFontFamily: string;
2497
+ placeholderColor: string;
2498
+ disabledOpacity: string;
2499
+ }
2500
+
2501
+ const fuComp = components.fileupload as Record<string, any> | undefined;
2502
+ const fuSizeMd = (fuComp?.size as Record<string, any>)?.['md'] ?? {};
2503
+ const fuContainer = fuComp?.container ?? {};
2504
+ const fuLabel = fuComp?.label ?? {};
2505
+ const fuButton = fuComp?.button ?? {};
2506
+ const fuPlaceholder = fuComp?.placeholder ?? {};
2507
+ const fuDisabled = fuComp?.disabled ?? {};
2508
+ const fuPadding = String(fuSizeMd.padding ?? '').split(/\s+/);
2509
+ const fuPaddingY = fuPadding[0] ?? '6px';
2510
+ const fuPaddingX = fuPadding[1] ?? fuPadding[0] ?? '16px';
2511
+ export const FILEUPLOAD_TOKENS: FileUploadTokens = {
2512
+ containerHeight: fuSizeMd.height ?? '40px',
2513
+ containerBorderRadius: fuContainer.borderRadius ?? fuSizeMd.borderRadius ?? '8px',
2514
+ containerBackground: fuContainer.background ?? '#ffffff',
2515
+ containerBackgroundWhite: fuContainer.backgroundWhite ?? fuContainer.background ?? '#ffffff',
2516
+ containerBorder: fuContainer.border ?? '1px solid #d1d5db',
2517
+ containerBorderFocus: fuContainer.borderFocus ?? '2px solid #2563eb',
2518
+ containerBorderError: fuContainer.borderError ?? '2px solid #dc2626',
2519
+ containerPaddingY: fuPaddingY,
2520
+ containerPaddingX: fuPaddingX,
2521
+ containerGap: fuContainer.gap ?? '12px',
2522
+ labelFontSize: fuLabel.fontSize ?? '14px',
2523
+ labelFontWeight:
2524
+ typeof fuLabel.fontWeight === 'string' ?
2525
+ parseInt(fuLabel.fontWeight, 10)
2526
+ : (fuLabel.fontWeight ?? 500),
2527
+ labelFontFamily: fuLabel.fontFamily ?? '"Inter", sans-serif',
2528
+ labelColor: fuLabel.color ?? '#374151',
2529
+ labelRequiredColor: fuLabel.requiredColor ?? '#dc2626',
2530
+ labelMarginBottom: fuLabel.marginBottom ?? '8px',
2531
+ buttonFontSize: fuSizeMd.fontSize ?? fuButton.fontSize ?? '14px',
2532
+ buttonFontWeight:
2533
+ typeof fuButton.fontWeight === 'string' ?
2534
+ parseInt(fuButton.fontWeight, 10)
2535
+ : (fuButton.fontWeight ?? 500),
2536
+ buttonFontFamily: fuButton.fontFamily ?? '"Inter", sans-serif',
2537
+ buttonColor: fuButton.color ?? '#2563eb',
2538
+ buttonBackground: fuButton.background ?? '#ffffff',
2539
+ buttonBorder: fuButton.border ?? '1px solid #d1d5db',
2540
+ buttonBorderRadius: fuButton.borderRadius ?? fuSizeMd.borderRadius ?? '4px',
2541
+ buttonHeight: fuSizeMd.height ?? fuButton.height ?? '40px',
2542
+ buttonPaddingY: fuPaddingY,
2543
+ buttonPaddingX: fuPaddingX,
2544
+ placeholderFontSize: fuSizeMd.fontSize ?? fuPlaceholder.fontSize ?? '14px',
2545
+ placeholderFontFamily: fuPlaceholder.fontFamily ?? '"Inter", sans-serif',
2546
+ placeholderColor: fuPlaceholder.color ?? '#6b7280',
2547
+ disabledOpacity: fuDisabled.opacity ?? '0.5',
2548
+ };
2549
+
2550
+ const defaultFileUploadTokens = FILEUPLOAD_TOKENS;
2551
+
2552
+ export type FileUploadSize = 'sm' | 'md' | 'lg';
2553
+
2554
+ /** Build FileUpload tokens from resolved tokens (e.g. from useTokens()). */
2555
+ export function getFileUploadTokensFromResolved(
2556
+ resolved: ResolvedTokens,
2557
+ size: FileUploadSize = 'md'
2558
+ ): FileUploadTokens {
2559
+ const fu = resolved.components?.fileupload as Record<string, any> | undefined;
2560
+ if (!fu) return defaultFileUploadTokens;
2561
+ const sizeTokens = (fu.size ?? {})[size] ?? fu.size?.md ?? {};
2562
+ const paddingParts = String(sizeTokens.padding ?? '').split(/\s+/);
2563
+ const paddingY = paddingParts[0] ?? defaultFileUploadTokens.containerPaddingY;
2564
+ const paddingX = paddingParts[1] ?? paddingParts[0] ?? defaultFileUploadTokens.containerPaddingX;
2565
+ const c = fu.container ?? {};
2566
+ const l = fu.label ?? {};
2567
+ const b = fu.button ?? {};
2568
+ const p = fu.placeholder ?? {};
2569
+ const d = fu.disabled ?? {};
2570
+ return {
2571
+ containerHeight: sizeTokens.height ?? defaultFileUploadTokens.containerHeight,
2572
+ containerBorderRadius:
2573
+ c.borderRadius ?? sizeTokens.borderRadius ?? defaultFileUploadTokens.containerBorderRadius,
2574
+ containerBackground: c.background ?? defaultFileUploadTokens.containerBackground,
2575
+ containerBackgroundWhite: c.backgroundWhite ?? defaultFileUploadTokens.containerBackgroundWhite,
2576
+ containerBorder: c.border ?? defaultFileUploadTokens.containerBorder,
2577
+ containerBorderFocus: c.borderFocus ?? defaultFileUploadTokens.containerBorderFocus,
2578
+ containerBorderError: c.borderError ?? defaultFileUploadTokens.containerBorderError,
2579
+ containerPaddingY: paddingY,
2580
+ containerPaddingX: paddingX,
2581
+ containerGap: c.gap ?? defaultFileUploadTokens.containerGap,
2582
+ labelFontSize: l.fontSize ?? defaultFileUploadTokens.labelFontSize,
2583
+ labelFontWeight:
2584
+ typeof l.fontWeight === 'string' ?
2585
+ parseInt(l.fontWeight, 10)
2586
+ : (l.fontWeight ?? defaultFileUploadTokens.labelFontWeight),
2587
+ labelFontFamily: l.fontFamily ?? defaultFileUploadTokens.labelFontFamily,
2588
+ labelColor: l.color ?? defaultFileUploadTokens.labelColor,
2589
+ labelRequiredColor: l.requiredColor ?? defaultFileUploadTokens.labelRequiredColor,
2590
+ labelMarginBottom: l.marginBottom ?? defaultFileUploadTokens.labelMarginBottom,
2591
+ buttonFontSize: sizeTokens.fontSize ?? defaultFileUploadTokens.buttonFontSize,
2592
+ buttonFontWeight:
2593
+ typeof b.fontWeight === 'string' ?
2594
+ parseInt(b.fontWeight, 10)
2595
+ : (b.fontWeight ?? defaultFileUploadTokens.buttonFontWeight),
2596
+ buttonFontFamily: b.fontFamily ?? defaultFileUploadTokens.buttonFontFamily,
2597
+ buttonColor: b.color ?? defaultFileUploadTokens.buttonColor,
2598
+ buttonBackground: b.background ?? defaultFileUploadTokens.buttonBackground,
2599
+ buttonBorder: b.border ?? defaultFileUploadTokens.buttonBorder,
2600
+ buttonBorderRadius:
2601
+ b.borderRadius ?? sizeTokens.borderRadius ?? defaultFileUploadTokens.buttonBorderRadius,
2602
+ buttonHeight: sizeTokens.height ?? defaultFileUploadTokens.buttonHeight,
2603
+ buttonPaddingY: paddingY,
2604
+ buttonPaddingX: paddingX,
2605
+ placeholderFontSize:
2606
+ p.fontSize ?? sizeTokens.fontSize ?? defaultFileUploadTokens.placeholderFontSize,
2607
+ placeholderFontFamily: p.fontFamily ?? defaultFileUploadTokens.placeholderFontFamily,
2608
+ placeholderColor: p.color ?? defaultFileUploadTokens.placeholderColor,
2609
+ disabledOpacity: d.opacity ?? defaultFileUploadTokens.disabledOpacity,
2610
+ };
2611
+ }
2612
+
2613
+ // UploadDropzone tokens (from components/uploaddropzone.json)
2614
+ export interface UploadDropzoneTokens {
2615
+ titleFontSize: string;
2616
+ titleFontWeight: number;
2617
+ titleFontFamily: string;
2618
+ titleLineHeight: string;
2619
+ titleColor: string;
2620
+ titleMarginBottom: string;
2621
+ zoneBackground: string;
2622
+ zoneBorder: string;
2623
+ zoneBorderActive: string;
2624
+ zoneBackgroundActive: string;
2625
+ zoneBorderRadius: string;
2626
+ zonePaddingY: string;
2627
+ zonePaddingX: string;
2628
+ zoneGap: string;
2629
+ iconSize: string;
2630
+ iconMarginBottom: string;
2631
+ iconBackground: string;
2632
+ iconColor: string;
2633
+ iconBorderRadius: string;
2634
+ iconBorder: string;
2635
+ instructionFontSize: string;
2636
+ instructionFontWeight: number;
2637
+ instructionFontFamily: string;
2638
+ instructionLineHeight: string;
2639
+ instructionColor: string;
2640
+ instructionMarginBottom: string;
2641
+ hintFontSize: string;
2642
+ hintFontWeight: number;
2643
+ hintFontFamily: string;
2644
+ hintLineHeight: string;
2645
+ hintColor: string;
2646
+ hintMarginBottom: string;
2647
+ buttonFontSize: string;
2648
+ buttonFontWeight: number;
2649
+ buttonFontFamily: string;
2650
+ buttonColor: string;
2651
+ buttonBackground: string;
2652
+ buttonBorder: string;
2653
+ buttonBorderRadius: string;
2654
+ buttonPaddingY: string;
2655
+ buttonPaddingX: string;
2656
+ selectedFileFontSize: string;
2657
+ selectedFileFontFamily: string;
2658
+ selectedFileColor: string;
2659
+ selectedFileMarginTop: string;
2660
+ disabledOpacity: string;
2661
+ }
2662
+
2663
+ const udComp = components.uploaddropzone as Record<string, any> | undefined;
2664
+ const udTitle = udComp?.title ?? {};
2665
+ const udZone = udComp?.zone ?? {};
2666
+ const udIcon = udComp?.icon ?? {};
2667
+ const udInstruction = udComp?.instruction ?? {};
2668
+ const udHint = udComp?.hint ?? {};
2669
+ const udButton = udComp?.button ?? {};
2670
+ const udSelectedFile = udComp?.selectedFile ?? {};
2671
+ const udDisabled = udComp?.disabled ?? {};
2672
+ export const UPLOADDROPZONE_TOKENS: UploadDropzoneTokens = {
2673
+ titleFontSize: udTitle.fontSize ?? '16px',
2674
+ titleFontWeight:
2675
+ typeof udTitle.fontWeight === 'string' ?
2676
+ parseInt(udTitle.fontWeight, 10)
2677
+ : (udTitle.fontWeight ?? 600),
2678
+ titleFontFamily: udTitle.fontFamily ?? '"Inter", sans-serif',
2679
+ titleLineHeight: udTitle.lineHeight ?? '1.5',
2680
+ titleColor: udTitle.color ?? '#1f1f1f',
2681
+ titleMarginBottom: udTitle.marginBottom ?? '12px',
2682
+ zoneBackground: udZone.background ?? '#f9fafb',
2683
+ zoneBorder: udZone.border ?? '2px dashed #c8d0d5',
2684
+ zoneBorderActive: udZone.borderActive ?? '2px dashed #00545e',
2685
+ zoneBackgroundActive: udZone.backgroundActive ?? '#dbeafe',
2686
+ zoneBorderRadius: udZone.borderRadius ?? '8px',
2687
+ zonePaddingY: udZone.paddingY ?? '24px',
2688
+ zonePaddingX: udZone.paddingX ?? '24px',
2689
+ zoneGap: udZone.gap ?? '0px',
2690
+ iconSize: udIcon.size ?? '48px',
2691
+ iconMarginBottom: udIcon.marginBottom ?? '12px',
2692
+ iconBackground: udIcon.background ?? '#dbeafe',
2693
+ iconColor: udIcon.color ?? '#00545e',
2694
+ iconBorderRadius: udIcon.borderRadius ?? '8px',
2695
+ iconBorder: udIcon.border ?? '1px solid #00545e',
2696
+ instructionFontSize: udInstruction.fontSize ?? '14px',
2697
+ instructionFontWeight:
2698
+ typeof udInstruction.fontWeight === 'string' ?
2699
+ parseInt(udInstruction.fontWeight, 10)
2700
+ : (udInstruction.fontWeight ?? 500),
2701
+ instructionFontFamily: udInstruction.fontFamily ?? '"Inter", sans-serif',
2702
+ instructionLineHeight: udInstruction.lineHeight ?? '1.5',
2703
+ instructionColor: udInstruction.color ?? '#1f1f1f',
2704
+ instructionMarginBottom: udInstruction.marginBottom ?? '8px',
2705
+ hintFontSize: udHint.fontSize ?? '12px',
2706
+ hintFontWeight:
2707
+ typeof udHint.fontWeight === 'string' ?
2708
+ parseInt(udHint.fontWeight, 10)
2709
+ : (udHint.fontWeight ?? 400),
2710
+ hintFontFamily: udHint.fontFamily ?? '"Inter", sans-serif',
2711
+ hintLineHeight: udHint.lineHeight ?? '1.5',
2712
+ hintColor: udHint.color ?? '#5a6b77',
2713
+ hintMarginBottom: udHint.marginBottom ?? '12px',
2714
+ buttonFontSize: udButton.fontSize ?? '12px',
2715
+ buttonFontWeight:
2716
+ typeof udButton.fontWeight === 'string' ?
2717
+ parseInt(udButton.fontWeight, 10)
2718
+ : (udButton.fontWeight ?? 400),
2719
+ buttonFontFamily: udButton.fontFamily ?? '"Inter", sans-serif',
2720
+ buttonColor: udButton.color ?? '#00545e',
2721
+ buttonBackground: udButton.background ?? '#ffffff',
2722
+ buttonBorder: udButton.border ?? '1px solid #00545e',
2723
+ buttonBorderRadius: udButton.borderRadius ?? '50px',
2724
+ buttonPaddingY: udButton.paddingY ?? '8px',
2725
+ buttonPaddingX: udButton.paddingX ?? '24px',
2726
+ selectedFileFontSize: udSelectedFile.fontSize ?? '12px',
2727
+ selectedFileFontFamily: udSelectedFile.fontFamily ?? '"Inter", sans-serif',
2728
+ selectedFileColor: udSelectedFile.color ?? '#5a6b77',
2729
+ selectedFileMarginTop: udSelectedFile.marginTop ?? '16px',
2730
+ disabledOpacity: udDisabled.opacity ?? '0.5',
2731
+ };
2732
+
2733
+ const defaultUploadDropzoneTokens = UPLOADDROPZONE_TOKENS;
2734
+
2735
+ // PhoneField tokens (from components/phonefield.json)
2736
+ export interface PhoneFieldTokens {
2737
+ borderRadiusSm: string;
2738
+ borderRadiusMd: string;
2739
+ borderRadiusLg: string;
2740
+ selectorWidthSm: string;
2741
+ selectorWidthMd: string;
2742
+ selectorWidthLg: string;
2743
+ labelFontSize: string;
2744
+ labelFontWeight: number;
2745
+ labelMarginBottom: string;
2746
+ labelColor: string;
2747
+ labelRequiredColor: string;
2748
+ fontFamily: string;
2749
+ disabledOpacity: string;
2750
+ outlineDefault: string;
2751
+ outlineError: string;
2752
+ selectorBackground: string;
2753
+ selectorBackgroundWhite: string;
2754
+ selectorBorderRight: string;
2755
+ }
2756
+
2757
+ const pfComp = components.phonefield as Record<string, any> | undefined;
2758
+ const pfSize = pfComp?.size ?? {};
2759
+ const pfSizeSm = pfSize.sm ?? {};
2760
+ const pfSizeMd = pfSize.md ?? {};
2761
+ const pfSizeLg = pfSize.lg ?? {};
2762
+ const pfLabel = pfComp?.label ?? {};
2763
+ const pfContainer = pfComp?.container ?? {};
2764
+ const pfSelector = pfComp?.selector ?? {};
2765
+ export const PHONEFIELD_TOKENS: PhoneFieldTokens = {
2766
+ borderRadiusSm: pfSizeSm.borderRadius ?? '8px',
2767
+ borderRadiusMd: pfSizeMd.borderRadius ?? '12px',
2768
+ borderRadiusLg: pfSizeLg.borderRadius ?? '12px',
2769
+ selectorWidthSm: String(pfSizeSm.selectorWidth ?? 100),
2770
+ selectorWidthMd: String(pfSizeMd.selectorWidth ?? 110),
2771
+ selectorWidthLg: String(pfSizeLg.selectorWidth ?? 120),
2772
+ labelFontSize: pfLabel.fontSize ?? '14px',
2773
+ labelFontWeight:
2774
+ typeof pfLabel.fontWeight === 'string' ?
2775
+ parseInt(pfLabel.fontWeight, 10)
2776
+ : (pfLabel.fontWeight ?? 500),
2777
+ labelMarginBottom: pfLabel.marginBottom ?? '8px',
2778
+ labelColor: pfLabel.color ?? '#374151',
2779
+ labelRequiredColor: pfLabel.requiredColor ?? '#dc2626',
2780
+ fontFamily: pfComp?.fontFamily ?? '"Inter", sans-serif',
2781
+ disabledOpacity: pfComp?.disabledOpacity ?? '0.5',
2782
+ outlineDefault: pfComp?.outline?.default ?? pfContainer.border ?? '2px solid transparent',
2783
+ outlineError: pfComp?.outline?.error ?? pfContainer.borderError ?? '2px solid #dc2626',
2784
+ selectorBackground: pfSelector.background ?? '#F5F5F5',
2785
+ selectorBackgroundWhite: pfSelector.backgroundWhite ?? pfSelector.background ?? '#F5F5F5',
2786
+ selectorBorderRight: pfSelector.borderRight ?? '1px solid #E0E0E0',
2787
+ };
2788
+
2789
+ const defaultPhoneFieldTokens = PHONEFIELD_TOKENS;
2790
+
2791
+ /** Build PhoneField tokens from resolved tokens (e.g. from useTokens()). */
2792
+ export function getPhoneFieldTokensFromResolved(resolved: ResolvedTokens): PhoneFieldTokens {
2793
+ const pf = resolved.components?.phonefield as Record<string, any> | undefined;
2794
+ if (!pf) return defaultPhoneFieldTokens;
2795
+ const size = pf.size ?? {};
2796
+ const sm = size.sm ?? {};
2797
+ const md = size.md ?? {};
2798
+ const lg = size.lg ?? {};
2799
+ const label = pf.label ?? {};
2800
+ const container = pf.container ?? {};
2801
+ const selector = pf.selector ?? {};
2802
+ return {
2803
+ borderRadiusSm: sm.borderRadius ?? defaultPhoneFieldTokens.borderRadiusSm,
2804
+ borderRadiusMd: md.borderRadius ?? defaultPhoneFieldTokens.borderRadiusMd,
2805
+ borderRadiusLg: lg.borderRadius ?? defaultPhoneFieldTokens.borderRadiusLg,
2806
+ selectorWidthSm: String(sm.selectorWidth ?? defaultPhoneFieldTokens.selectorWidthSm),
2807
+ selectorWidthMd: String(md.selectorWidth ?? defaultPhoneFieldTokens.selectorWidthMd),
2808
+ selectorWidthLg: String(lg.selectorWidth ?? defaultPhoneFieldTokens.selectorWidthLg),
2809
+ labelFontSize: label.fontSize ?? defaultPhoneFieldTokens.labelFontSize,
2810
+ labelFontWeight:
2811
+ typeof label.fontWeight === 'string' ?
2812
+ parseInt(label.fontWeight, 10)
2813
+ : (label.fontWeight ?? defaultPhoneFieldTokens.labelFontWeight),
2814
+ labelMarginBottom: label.marginBottom ?? defaultPhoneFieldTokens.labelMarginBottom,
2815
+ labelColor: label.color ?? defaultPhoneFieldTokens.labelColor,
2816
+ labelRequiredColor: label.requiredColor ?? defaultPhoneFieldTokens.labelRequiredColor,
2817
+ fontFamily: pf.fontFamily ?? defaultPhoneFieldTokens.fontFamily,
2818
+ disabledOpacity: pf.disabledOpacity ?? defaultPhoneFieldTokens.disabledOpacity,
2819
+ outlineDefault: container.border ?? defaultPhoneFieldTokens.outlineDefault,
2820
+ outlineError: container.borderError ?? defaultPhoneFieldTokens.outlineError,
2821
+ selectorBackground: selector.background ?? defaultPhoneFieldTokens.selectorBackground,
2822
+ selectorBackgroundWhite:
2823
+ selector.backgroundWhite ?? defaultPhoneFieldTokens.selectorBackgroundWhite,
2824
+ selectorBorderRight: selector.borderRight ?? defaultPhoneFieldTokens.selectorBorderRight,
2825
+ };
2826
+ }
2827
+
2828
+ // Badge tokens (from components/badge.json)
2829
+ export type BadgeSize = 'xs' | 'sm' | 'md' | 'lg';
2830
+
2831
+ export interface BadgeTokens {
2832
+ heightXs: string;
2833
+ heightSm: string;
2834
+ heightMd: string;
2835
+ heightLg: string;
2836
+ paddingXs: string;
2837
+ paddingSm: string;
2838
+ paddingMd: string;
2839
+ paddingLg: string;
2840
+ fontSizeXs: string;
2841
+ fontSizeSm: string;
2842
+ fontSizeMd: string;
2843
+ fontSizeLg: string;
2844
+ borderRadiusXs: string;
2845
+ borderRadiusSm: string;
2846
+ borderRadiusMd: string;
2847
+ borderRadiusLg: string;
2848
+ removeIconSizeXs: number;
2849
+ removeIconSizeSm: number;
2850
+ removeIconSizeMd: number;
2851
+ removeIconSizeLg: number;
2852
+ background: string;
2853
+ backgroundWhite: string;
2854
+ color: string;
2855
+ removeButtonColor: string;
2856
+ removeButtonHoverColor: string;
2857
+ boxShadow: string;
2858
+ borderColor: string;
2859
+ }
2860
+
2861
+ const badgeComp = components.badge as Record<string, any> | undefined;
2862
+ const badgeSize = badgeComp?.size ?? {};
2863
+ const badgeSizeXs = badgeSize.xs ?? {};
2864
+ const badgeSizeSm = badgeSize.sm ?? {};
2865
+ const badgeSizeMd = badgeSize.md ?? {};
2866
+ const badgeSizeLg = badgeSize.lg ?? {};
2867
+ export const BADGE_TOKENS: BadgeTokens = {
2868
+ heightXs: badgeSizeXs.height ?? '20px',
2869
+ heightSm: badgeSizeSm.height ?? '28px',
2870
+ heightMd: badgeSizeMd.height ?? '40px',
2871
+ heightLg: badgeSizeLg.height ?? '44px',
2872
+ paddingXs: badgeSizeXs.padding ?? '2px 6px',
2873
+ paddingSm: badgeSizeSm.padding ?? '4px 8px',
2874
+ paddingMd: badgeSizeMd.padding ?? '7px 12px 7px 8px',
2875
+ paddingLg: badgeSizeLg.padding ?? '8px 14px 8px 10px',
2876
+ fontSizeXs: badgeSizeXs.fontSize ?? '10px',
2877
+ fontSizeSm: badgeSizeSm.fontSize ?? '12px',
2878
+ fontSizeMd: badgeSizeMd.fontSize ?? '14px',
2879
+ fontSizeLg: badgeSizeLg.fontSize ?? '14px',
2880
+ borderRadiusXs: badgeSizeXs.borderRadius ?? '50px',
2881
+ borderRadiusSm: badgeSizeSm.borderRadius ?? '50px',
2882
+ borderRadiusMd: badgeSizeMd.borderRadius ?? '50px',
2883
+ borderRadiusLg: badgeSizeLg.borderRadius ?? '50px',
2884
+ removeIconSizeXs: parsePxToNumber(badgeSizeXs.removeIconSize) ?? 8,
2885
+ removeIconSizeSm: parsePxToNumber(badgeSizeSm.removeIconSize) ?? 10,
2886
+ removeIconSizeMd: parsePxToNumber(badgeSizeMd.removeIconSize) ?? 12,
2887
+ removeIconSizeLg: parsePxToNumber(badgeSizeLg.removeIconSize) ?? 14,
2888
+ background: badgeComp?.background ?? '#ffffff',
2889
+ backgroundWhite: badgeComp?.backgroundWhite ?? '#ffffff',
2890
+ color: badgeComp?.color ?? '#161616',
2891
+ removeButtonColor: badgeComp?.removeButtonColor ?? '#161616',
2892
+ removeButtonHoverColor: badgeComp?.removeButtonHoverColor ?? '#6b7280',
2893
+ boxShadow: badgeComp?.boxShadow ?? '0 1px 2px rgba(0, 0, 0, 0.05)',
2894
+ borderColor: badgeComp?.borderColor ?? '#E7EBEE',
2895
+ };
2896
+
2897
+ const defaultBadgeTokens = BADGE_TOKENS;
2898
+
2899
+ /** Build Badge tokens from resolved tokens (e.g. from useTokens()). */
2900
+ export function getBadgeTokensFromResolved(resolved: ResolvedTokens): BadgeTokens {
2901
+ const b = resolved.components?.badge as Record<string, any> | undefined;
2902
+ if (!b) return defaultBadgeTokens;
2903
+ const size = b.size ?? {};
2904
+ const xs = size.xs ?? {};
2905
+ const sm = size.sm ?? {};
2906
+ const md = size.md ?? {};
2907
+ const lg = size.lg ?? {};
2908
+ return {
2909
+ heightXs: xs.height ?? defaultBadgeTokens.heightXs,
2910
+ heightSm: sm.height ?? defaultBadgeTokens.heightSm,
2911
+ heightMd: md.height ?? defaultBadgeTokens.heightMd,
2912
+ heightLg: lg.height ?? defaultBadgeTokens.heightLg,
2913
+ paddingXs: xs.padding ?? defaultBadgeTokens.paddingXs,
2914
+ paddingSm: sm.padding ?? defaultBadgeTokens.paddingSm,
2915
+ paddingMd: md.padding ?? defaultBadgeTokens.paddingMd,
2916
+ paddingLg: lg.padding ?? defaultBadgeTokens.paddingLg,
2917
+ fontSizeXs: xs.fontSize ?? defaultBadgeTokens.fontSizeXs,
2918
+ fontSizeSm: sm.fontSize ?? defaultBadgeTokens.fontSizeSm,
2919
+ fontSizeMd: md.fontSize ?? defaultBadgeTokens.fontSizeMd,
2920
+ fontSizeLg: lg.fontSize ?? defaultBadgeTokens.fontSizeLg,
2921
+ borderRadiusXs: xs.borderRadius ?? defaultBadgeTokens.borderRadiusXs,
2922
+ borderRadiusSm: sm.borderRadius ?? defaultBadgeTokens.borderRadiusSm,
2923
+ borderRadiusMd: md.borderRadius ?? defaultBadgeTokens.borderRadiusMd,
2924
+ borderRadiusLg: lg.borderRadius ?? defaultBadgeTokens.borderRadiusLg,
2925
+ removeIconSizeXs: parsePxToNumber(xs.removeIconSize) ?? defaultBadgeTokens.removeIconSizeXs,
2926
+ removeIconSizeSm: parsePxToNumber(sm.removeIconSize) ?? defaultBadgeTokens.removeIconSizeSm,
2927
+ removeIconSizeMd: parsePxToNumber(md.removeIconSize) ?? defaultBadgeTokens.removeIconSizeMd,
2928
+ removeIconSizeLg: parsePxToNumber(lg.removeIconSize) ?? defaultBadgeTokens.removeIconSizeLg,
2929
+ background: b.background ?? defaultBadgeTokens.background,
2930
+ backgroundWhite: b.backgroundWhite ?? defaultBadgeTokens.backgroundWhite,
2931
+ color: b.color ?? defaultBadgeTokens.color,
2932
+ removeButtonColor: b.removeButtonColor ?? defaultBadgeTokens.removeButtonColor,
2933
+ removeButtonHoverColor: b.removeButtonHoverColor ?? defaultBadgeTokens.removeButtonHoverColor,
2934
+ boxShadow: b.boxShadow ?? defaultBadgeTokens.boxShadow,
2935
+ borderColor: b.borderColor ?? defaultBadgeTokens.borderColor,
2936
+ };
2937
+ }
2938
+
2939
+ // FilterPopover tokens (from components/filterpopover.json)
2940
+ export interface FilterPopoverTokens {
2941
+ minWidth: string;
2942
+ minHeight: string;
2943
+ maxHeight: string;
2944
+ padding: string;
2945
+ background: string;
2946
+ border: string;
2947
+ borderRadius: string;
2948
+ innerBorderRadius: string;
2949
+ boxShadow: string;
2950
+ headerBackground: string;
2951
+ headerPadding: string;
2952
+ headerTitleFontSize: string;
2953
+ headerTitleFontWeight: string;
2954
+ headerTitleColor: string;
2955
+ headerMarginBottom: string;
2956
+ contentPadding: string;
2957
+ contentGap: string;
2958
+ footerPadding: string;
2959
+ footerMarginTop: string;
2960
+ footerGap: string;
2961
+ }
2962
+
2963
+ const fpComp = components.filterpopover as Record<string, any> | undefined;
2964
+ const fpHeader = fpComp?.header ?? {};
2965
+ const fpContent = fpComp?.content ?? {};
2966
+ const fpFooter = fpComp?.footer ?? {};
2967
+ export const FILTERPOPOVER_TOKENS: FilterPopoverTokens = {
2968
+ minWidth: fpComp?.minWidth ?? '380px',
2969
+ minHeight: fpComp?.minHeight ?? '1px',
2970
+ maxHeight: fpComp?.maxHeight ?? 'calc(100vh - 156px)',
2971
+ padding: fpComp?.padding ?? '16px',
2972
+ background: fpComp?.background ?? '#FFFFFF',
2973
+ border: fpComp?.border ?? '1px solid #E7EBEE',
2974
+ borderRadius: fpComp?.borderRadius ?? '16px',
2975
+ innerBorderRadius: fpComp?.innerBorderRadius ?? '16px 16px 0 0',
2976
+ boxShadow:
2977
+ fpComp?.boxShadow ??
2978
+ '0px 3px 15px -3px rgba(16, 24, 40, 0.1), 0px 2px 8px -4px rgba(16, 24, 40, 0.1)',
2979
+ headerBackground: fpHeader.background ?? '#FAF8F4',
2980
+ headerPadding: fpHeader.padding ?? '16px',
2981
+ headerTitleFontSize: fpHeader.titleFontSize ?? '16px',
2982
+ headerTitleFontWeight: fpHeader.titleFontWeight ?? '500',
2983
+ headerTitleColor: fpHeader.titleColor ?? '#1F1F1F',
2984
+ headerMarginBottom: fpHeader.marginBottom ?? '0px',
2985
+ contentPadding: fpContent.padding ?? '16px',
2986
+ contentGap: fpContent.gap ?? '12px',
2987
+ footerPadding: fpFooter.padding ?? '16px',
2988
+ footerMarginTop: fpFooter.marginTop ?? '0px',
2989
+ footerGap: fpFooter.gap ?? '16px',
2990
+ };
2991
+
2992
+ /** Build FilterPopover tokens from resolved tokens (e.g. from useTokens()). Responsive values (minWidth, minHeight, padding, borderRadius) update automatically per breakpoint. */
2993
+ export function getFilterPopoverTokensFromResolved(resolved: ResolvedTokens): FilterPopoverTokens {
2994
+ const fp = resolved.components?.filterpopover as Record<string, any> | undefined;
2995
+ if (!fp) return FILTERPOPOVER_TOKENS;
2996
+ const header = fp.header ?? {};
2997
+ const content = fp.content ?? {};
2998
+ const footer = fp.footer ?? {};
2999
+ return {
3000
+ minWidth: fp.minWidth ?? FILTERPOPOVER_TOKENS.minWidth,
3001
+ minHeight: fp.minHeight ?? FILTERPOPOVER_TOKENS.minHeight,
3002
+ maxHeight: fp.maxHeight ?? FILTERPOPOVER_TOKENS.maxHeight,
3003
+ padding: fp.padding ?? FILTERPOPOVER_TOKENS.padding,
3004
+ background: fp.background ?? FILTERPOPOVER_TOKENS.background,
3005
+ border: fp.border ?? FILTERPOPOVER_TOKENS.border,
3006
+ borderRadius: fp.borderRadius ?? FILTERPOPOVER_TOKENS.borderRadius,
3007
+ innerBorderRadius: fp.innerBorderRadius ?? FILTERPOPOVER_TOKENS.innerBorderRadius,
3008
+ boxShadow: fp.boxShadow ?? FILTERPOPOVER_TOKENS.boxShadow,
3009
+ headerBackground: header.background ?? FILTERPOPOVER_TOKENS.headerBackground,
3010
+ headerPadding: header.padding ?? FILTERPOPOVER_TOKENS.headerPadding,
3011
+ headerTitleFontSize: header.titleFontSize ?? FILTERPOPOVER_TOKENS.headerTitleFontSize,
3012
+ headerTitleFontWeight: header.titleFontWeight ?? FILTERPOPOVER_TOKENS.headerTitleFontWeight,
3013
+ headerTitleColor: header.titleColor ?? FILTERPOPOVER_TOKENS.headerTitleColor,
3014
+ headerMarginBottom: header.marginBottom ?? FILTERPOPOVER_TOKENS.headerMarginBottom,
3015
+ contentPadding: content.padding ?? FILTERPOPOVER_TOKENS.contentPadding,
3016
+ contentGap: content.gap ?? FILTERPOPOVER_TOKENS.contentGap,
3017
+ footerPadding: footer.padding ?? FILTERPOPOVER_TOKENS.footerPadding,
3018
+ footerMarginTop: footer.marginTop ?? FILTERPOPOVER_TOKENS.footerMarginTop,
3019
+ footerGap: footer.gap ?? FILTERPOPOVER_TOKENS.footerGap,
3020
+ };
3021
+ }
3022
+
3023
+ /** Build UploadDropzone tokens from resolved tokens (e.g. from useTokens()). */
3024
+ export function getUploadDropzoneTokensFromResolved(
3025
+ resolved: ResolvedTokens
3026
+ ): UploadDropzoneTokens {
3027
+ const ud = resolved.components?.uploaddropzone as Record<string, any> | undefined;
3028
+ if (!ud) return defaultUploadDropzoneTokens;
3029
+ const t = ud.title ?? {};
3030
+ const z = ud.zone ?? {};
3031
+ const i = ud.icon ?? {};
3032
+ const inst = ud.instruction ?? {};
3033
+ const h = ud.hint ?? {};
3034
+ const b = ud.button ?? {};
3035
+ const sf = ud.selectedFile ?? {};
3036
+ const d = ud.disabled ?? {};
3037
+ return {
3038
+ titleFontSize: t.fontSize ?? defaultUploadDropzoneTokens.titleFontSize,
3039
+ titleFontWeight:
3040
+ typeof t.fontWeight === 'string' ?
3041
+ parseInt(t.fontWeight, 10)
3042
+ : (t.fontWeight ?? defaultUploadDropzoneTokens.titleFontWeight),
3043
+ titleFontFamily: t.fontFamily ?? defaultUploadDropzoneTokens.titleFontFamily,
3044
+ titleLineHeight: t.lineHeight ?? defaultUploadDropzoneTokens.titleLineHeight,
3045
+ titleColor: t.color ?? defaultUploadDropzoneTokens.titleColor,
3046
+ titleMarginBottom: t.marginBottom ?? defaultUploadDropzoneTokens.titleMarginBottom,
3047
+ zoneBackground: z.background ?? defaultUploadDropzoneTokens.zoneBackground,
3048
+ zoneBorder: z.border ?? defaultUploadDropzoneTokens.zoneBorder,
3049
+ zoneBorderActive: z.borderActive ?? defaultUploadDropzoneTokens.zoneBorderActive,
3050
+ zoneBackgroundActive: z.backgroundActive ?? defaultUploadDropzoneTokens.zoneBackgroundActive,
3051
+ zoneBorderRadius: z.borderRadius ?? defaultUploadDropzoneTokens.zoneBorderRadius,
3052
+ zonePaddingY: z.paddingY ?? defaultUploadDropzoneTokens.zonePaddingY,
3053
+ zonePaddingX: z.paddingX ?? defaultUploadDropzoneTokens.zonePaddingX,
3054
+ zoneGap: z.gap ?? defaultUploadDropzoneTokens.zoneGap,
3055
+ iconSize: i.size ?? defaultUploadDropzoneTokens.iconSize,
3056
+ iconMarginBottom: i.marginBottom ?? defaultUploadDropzoneTokens.iconMarginBottom,
3057
+ iconBackground: i.background ?? defaultUploadDropzoneTokens.iconBackground,
3058
+ iconColor: i.color ?? defaultUploadDropzoneTokens.iconColor,
3059
+ iconBorderRadius: i.borderRadius ?? defaultUploadDropzoneTokens.iconBorderRadius,
3060
+ iconBorder: i.border ?? defaultUploadDropzoneTokens.iconBorder,
3061
+ instructionFontSize: inst.fontSize ?? defaultUploadDropzoneTokens.instructionFontSize,
3062
+ instructionFontWeight:
3063
+ typeof inst.fontWeight === 'string' ?
3064
+ parseInt(inst.fontWeight, 10)
3065
+ : (inst.fontWeight ?? defaultUploadDropzoneTokens.instructionFontWeight),
3066
+ instructionFontFamily: inst.fontFamily ?? defaultUploadDropzoneTokens.instructionFontFamily,
3067
+ instructionLineHeight: inst.lineHeight ?? defaultUploadDropzoneTokens.instructionLineHeight,
3068
+ instructionColor: inst.color ?? defaultUploadDropzoneTokens.instructionColor,
3069
+ instructionMarginBottom:
3070
+ inst.marginBottom ?? defaultUploadDropzoneTokens.instructionMarginBottom,
3071
+ hintFontSize: h.fontSize ?? defaultUploadDropzoneTokens.hintFontSize,
3072
+ hintFontWeight:
3073
+ typeof h.fontWeight === 'string' ?
3074
+ parseInt(h.fontWeight, 10)
3075
+ : (h.fontWeight ?? defaultUploadDropzoneTokens.hintFontWeight),
3076
+ hintFontFamily: h.fontFamily ?? defaultUploadDropzoneTokens.hintFontFamily,
3077
+ hintLineHeight: h.lineHeight ?? defaultUploadDropzoneTokens.hintLineHeight,
3078
+ hintColor: h.color ?? defaultUploadDropzoneTokens.hintColor,
3079
+ hintMarginBottom: h.marginBottom ?? defaultUploadDropzoneTokens.hintMarginBottom,
3080
+ buttonFontSize: b.fontSize ?? defaultUploadDropzoneTokens.buttonFontSize,
3081
+ buttonFontWeight:
3082
+ typeof b.fontWeight === 'string' ?
3083
+ parseInt(b.fontWeight, 10)
3084
+ : (b.fontWeight ?? defaultUploadDropzoneTokens.buttonFontWeight),
3085
+ buttonFontFamily: b.fontFamily ?? defaultUploadDropzoneTokens.buttonFontFamily,
3086
+ buttonColor: b.color ?? defaultUploadDropzoneTokens.buttonColor,
3087
+ buttonBackground: b.background ?? defaultUploadDropzoneTokens.buttonBackground,
3088
+ buttonBorder: b.border ?? defaultUploadDropzoneTokens.buttonBorder,
3089
+ buttonBorderRadius: b.borderRadius ?? defaultUploadDropzoneTokens.buttonBorderRadius,
3090
+ buttonPaddingY: b.paddingY ?? defaultUploadDropzoneTokens.buttonPaddingY,
3091
+ buttonPaddingX: b.paddingX ?? defaultUploadDropzoneTokens.buttonPaddingX,
3092
+ selectedFileFontSize: sf.fontSize ?? defaultUploadDropzoneTokens.selectedFileFontSize,
3093
+ selectedFileFontFamily: sf.fontFamily ?? defaultUploadDropzoneTokens.selectedFileFontFamily,
3094
+ selectedFileColor: sf.color ?? defaultUploadDropzoneTokens.selectedFileColor,
3095
+ selectedFileMarginTop: sf.marginTop ?? defaultUploadDropzoneTokens.selectedFileMarginTop,
3096
+ disabledOpacity: d.opacity ?? defaultUploadDropzoneTokens.disabledOpacity,
3097
+ };
3098
+ }
3099
+
3100
+ // Loader token types (aligned with components/loader.json).
3101
+ export interface LoaderTokens {
3102
+ backdropBackground: string;
3103
+ backdropOpacity: string;
3104
+ backdropZIndex: string;
3105
+ dotColor: string;
3106
+ dotSize: string;
3107
+ dotActiveSpread: string;
3108
+ dotGap: string;
3109
+ }
3110
+
3111
+ const loaderComp = components.loader as Record<string, any> | undefined;
3112
+ export const LOADER_TOKENS: LoaderTokens = {
3113
+ backdropBackground: loaderComp?.backdrop?.background ?? '#000000',
3114
+ backdropOpacity: loaderComp?.backdrop?.opacity ?? '0.5',
3115
+ backdropZIndex: loaderComp?.backdrop?.zIndex ?? '9999',
3116
+ dotColor: loaderComp?.dot?.color ?? '#00545E',
3117
+ dotSize: loaderComp?.dot?.size ?? '4px',
3118
+ dotActiveSpread: loaderComp?.dot?.activeSpread ?? '4px',
3119
+ dotGap: loaderComp?.dot?.gap ?? '20px',
3120
+ };
3121
+
3122
+ const defaultLoaderTokens = LOADER_TOKENS;
3123
+
3124
+ /** Build Loader tokens from resolved tokens (e.g. from useTokens()). Use this so tokens update when theme mode (light/dark) or brand changes. */
3125
+ export function getLoaderTokensFromResolved(resolved: ResolvedTokens): LoaderTokens {
3126
+ const l = resolved.components?.loader as Record<string, any> | undefined;
3127
+ if (!l) return defaultLoaderTokens;
3128
+ return {
3129
+ backdropBackground: l.backdrop?.background ?? defaultLoaderTokens.backdropBackground,
3130
+ backdropOpacity: l.backdrop?.opacity ?? defaultLoaderTokens.backdropOpacity,
3131
+ backdropZIndex: l.backdrop?.zIndex ?? defaultLoaderTokens.backdropZIndex,
3132
+ dotColor: l.dot?.color ?? defaultLoaderTokens.dotColor,
3133
+ dotSize: l.dot?.size ?? defaultLoaderTokens.dotSize,
3134
+ dotActiveSpread: l.dot?.activeSpread ?? defaultLoaderTokens.dotActiveSpread,
3135
+ dotGap: l.dot?.gap ?? defaultLoaderTokens.dotGap,
3136
+ };
3137
+ }
3138
+
3139
+ // Skeleton token types (aligned with components/skeleton.json).
3140
+ export interface SkeletonTokens {
3141
+ baseColor: string;
3142
+ highlightColor: string;
3143
+ }
3144
+
3145
+ const skeletonComp = components.skeleton as Record<string, any> | undefined;
3146
+ export const SKELETON_TOKENS: SkeletonTokens = {
3147
+ baseColor: skeletonComp?.baseColor ?? '#e5e9eb',
3148
+ highlightColor: skeletonComp?.highlightColor ?? '#f3f5f6',
3149
+ };
3150
+
3151
+ const defaultSkeletonTokens = SKELETON_TOKENS;
3152
+
3153
+ /** Build Skeleton tokens from resolved tokens (e.g. from useTokens()). Use this so tokens update when theme mode (light/dark) or brand changes. */
3154
+ export function getSkeletonTokensFromResolved(resolved: ResolvedTokens): SkeletonTokens {
3155
+ const s = resolved.components?.skeleton as Record<string, any> | undefined;
3156
+ if (!s) return defaultSkeletonTokens;
3157
+ return {
3158
+ baseColor: s.baseColor ?? defaultSkeletonTokens.baseColor,
3159
+ highlightColor: s.highlightColor ?? defaultSkeletonTokens.highlightColor,
3160
+ };
3161
+ }
3162
+
3163
+ // DataTable token types (aligned with components/datatable.json).
3164
+ export interface DataTableTokens {
3165
+ headerBackground: string;
3166
+ headerColor: string;
3167
+ headerFontWeight: string;
3168
+ headerFontSize: string;
3169
+ headerPadding: string;
3170
+ headerBorderBottom: string;
3171
+ headerBorderRight: string;
3172
+ cellPadding: string;
3173
+ cellFontSize: string;
3174
+ cellColor: string;
3175
+ cellBorderBottom: string;
3176
+ rowBackground: string;
3177
+ rowStripeBackground: string;
3178
+ rowHoverBackground: string;
3179
+ skeletonBaseColor: string;
3180
+ skeletonHeight: string;
3181
+ skeletonRadius: string;
3182
+ emptyStateColor: string;
3183
+ emptyStateFontSize: string;
3184
+ tableBorder: string;
3185
+ sortIconWidth: string;
3186
+ sortIconHeight: string;
3187
+ }
3188
+
3189
+ const datatableComp = components.datatable as Record<string, any> | undefined;
3190
+ export const DATATABLE_TOKENS: DataTableTokens = {
3191
+ headerBackground: datatableComp?.header?.background ?? '#F9FAFB',
3192
+ headerColor: datatableComp?.header?.color ?? '#00545E',
3193
+ headerFontWeight: String(datatableComp?.header?.fontWeight ?? '600'),
3194
+ headerFontSize: datatableComp?.header?.fontSize ?? '12px',
3195
+ headerPadding: datatableComp?.header?.padding ?? '16px',
3196
+ headerBorderBottom: datatableComp?.header?.borderBottom ?? '#E5E7EB',
3197
+ headerBorderRight: datatableComp?.header?.borderRight ?? '#00545E',
3198
+ cellPadding: datatableComp?.cell?.padding ?? '16px',
3199
+ cellFontSize: datatableComp?.cell?.fontSize ?? '12px',
3200
+ cellColor: datatableComp?.cell?.color ?? 'rgba(0,0,0,0.87)',
3201
+ cellBorderBottom: datatableComp?.cell?.borderBottom ?? '#E5E7EB',
3202
+ rowBackground: datatableComp?.row?.background ?? '#FFFFFF',
3203
+ rowStripeBackground: datatableComp?.row?.stripeBackground ?? '#F3F5F6',
3204
+ rowHoverBackground: datatableComp?.row?.hoverBackground ?? '#F3F4F6',
3205
+ skeletonBaseColor: datatableComp?.skeleton?.baseColor ?? '#f3f4f6',
3206
+ skeletonHeight: datatableComp?.skeleton?.height ?? '16px',
3207
+ skeletonRadius: datatableComp?.skeleton?.borderRadius ?? '4px',
3208
+ emptyStateColor: datatableComp?.emptyState?.color ?? '#6B7280',
3209
+ emptyStateFontSize: datatableComp?.emptyState?.fontSize ?? '16px',
3210
+ tableBorder: datatableComp?.table?.border ?? '1px solid #E5E7EB',
3211
+ sortIconWidth: datatableComp?.sortIcon?.width ?? '16px',
3212
+ sortIconHeight: datatableComp?.sortIcon?.height ?? '16px',
3213
+ };
3214
+
3215
+ const defaultDataTableTokens = DATATABLE_TOKENS;
3216
+
3217
+ /** Build DataTable tokens from resolved tokens (e.g. from useTokens()). */
3218
+ export function getDataTableTokensFromResolved(resolved: ResolvedTokens): DataTableTokens {
3219
+ const d = resolved.components?.datatable as Record<string, any> | undefined;
3220
+ if (!d) return defaultDataTableTokens;
3221
+ return {
3222
+ headerBackground: d.header?.background ?? defaultDataTableTokens.headerBackground,
3223
+ headerColor: d.header?.color ?? defaultDataTableTokens.headerColor,
3224
+ headerFontWeight: String(d.header?.fontWeight ?? defaultDataTableTokens.headerFontWeight),
3225
+ headerFontSize: d.header?.fontSize ?? defaultDataTableTokens.headerFontSize,
3226
+ headerPadding: d.header?.padding ?? defaultDataTableTokens.headerPadding,
3227
+ headerBorderBottom: d.header?.borderBottom ?? defaultDataTableTokens.headerBorderBottom,
3228
+ headerBorderRight: d.header?.borderRight ?? defaultDataTableTokens.headerBorderRight,
3229
+ cellPadding: d.cell?.padding ?? defaultDataTableTokens.cellPadding,
3230
+ cellFontSize: d.cell?.fontSize ?? defaultDataTableTokens.cellFontSize,
3231
+ cellColor: d.cell?.color ?? defaultDataTableTokens.cellColor,
3232
+ cellBorderBottom: d.cell?.borderBottom ?? defaultDataTableTokens.cellBorderBottom,
3233
+ rowBackground: d.row?.background ?? defaultDataTableTokens.rowBackground,
3234
+ rowStripeBackground: d.row?.stripeBackground ?? defaultDataTableTokens.rowStripeBackground,
3235
+ rowHoverBackground: d.row?.hoverBackground ?? defaultDataTableTokens.rowHoverBackground,
3236
+ skeletonBaseColor: d.skeleton?.baseColor ?? defaultDataTableTokens.skeletonBaseColor,
3237
+ skeletonHeight: d.skeleton?.height ?? defaultDataTableTokens.skeletonHeight,
3238
+ skeletonRadius: d.skeleton?.borderRadius ?? defaultDataTableTokens.skeletonRadius,
3239
+ emptyStateColor: d.emptyState?.color ?? defaultDataTableTokens.emptyStateColor,
3240
+ emptyStateFontSize: d.emptyState?.fontSize ?? defaultDataTableTokens.emptyStateFontSize,
3241
+ tableBorder: d.table?.border ?? defaultDataTableTokens.tableBorder,
3242
+ sortIconWidth: d.sortIcon?.width ?? defaultDataTableTokens.sortIconWidth,
3243
+ sortIconHeight: d.sortIcon?.height ?? defaultDataTableTokens.sortIconHeight,
3244
+ };
3245
+ }
3246
+
3247
+ export interface ProgressTokens {
3248
+ height: string;
3249
+ borderRadius: string;
3250
+ trackBackground: string;
3251
+ barBackground: string;
3252
+ }
3253
+
3254
+ const progressComp = defaultResolved.components?.progress;
3255
+ export const PROGRESS_TOKENS: ProgressTokens = {
3256
+ height: progressComp?.height ?? '8px',
3257
+ borderRadius: progressComp?.borderRadius ?? '4px',
3258
+ trackBackground: progressComp?.track?.background ?? '#E0E0E0',
3259
+ barBackground: progressComp?.bar?.background ?? '#1976d2',
3260
+ };
3261
+
3262
+ export function getProgressTokensFromResolved(resolved: ResolvedTokens): ProgressTokens {
3263
+ const p = resolved.components?.progress as Record<string, any> | undefined;
3264
+ if (!p) return PROGRESS_TOKENS;
3265
+ return {
3266
+ height: p.height ?? PROGRESS_TOKENS.height,
3267
+ borderRadius: p.borderRadius ?? PROGRESS_TOKENS.borderRadius,
3268
+ trackBackground: p.track?.background ?? PROGRESS_TOKENS.trackBackground,
3269
+ barBackground: p.bar?.background ?? PROGRESS_TOKENS.barBackground,
3270
+ };
3271
+ }
3272
+
3273
+ export interface StatCardTokens {
3274
+ surfaceBackground: string;
3275
+ border: string;
3276
+ borderRadius: string;
3277
+ padding: string;
3278
+ gap: string;
3279
+ iconWellSize: string;
3280
+ iconWellBorderRadius: string;
3281
+ iconWellAccentMixPercent: string;
3282
+ labelFontSize: string;
3283
+ labelFontWeight: string | number;
3284
+ labelColor: string;
3285
+ valueFontSize: string;
3286
+ valueFontWeight: string | number;
3287
+ valueColor: string;
3288
+ }
3289
+
3290
+ const statCardComp = defaultResolved.components?.statCard as Record<string, any> | undefined;
3291
+
3292
+ export const STAT_CARD_TOKENS: StatCardTokens = {
3293
+ surfaceBackground: statCardComp?.surfaceBackground ?? '#ffffff',
3294
+ border: statCardComp?.border ?? '1px solid #E5E7EB',
3295
+ borderRadius: statCardComp?.borderRadius ?? '12px',
3296
+ padding: statCardComp?.padding ?? '12px 16px',
3297
+ gap: statCardComp?.gap ?? '12px',
3298
+ iconWellSize: statCardComp?.iconWell?.size ?? '34px',
3299
+ iconWellBorderRadius: statCardComp?.iconWell?.borderRadius ?? '8px',
3300
+ iconWellAccentMixPercent: String(statCardComp?.iconWell?.accentMixPercent ?? '14'),
3301
+ labelFontSize: statCardComp?.label?.fontSize ?? '12px',
3302
+ labelFontWeight: statCardComp?.label?.fontWeight ?? 600,
3303
+ labelColor: statCardComp?.label?.color ?? '#6B7280',
3304
+ valueFontSize: statCardComp?.value?.fontSize ?? '18px',
3305
+ valueFontWeight: statCardComp?.value?.fontWeight ?? 700,
3306
+ valueColor: statCardComp?.value?.color ?? '#111827',
3307
+ };
3308
+
3309
+ export function getStatCardTokensFromResolved(resolved: ResolvedTokens): StatCardTokens {
3310
+ const s = resolved.components?.statCard as Record<string, any> | undefined;
3311
+ if (!s) return STAT_CARD_TOKENS;
3312
+ const well = s.iconWell ?? {};
3313
+ const label = s.label ?? {};
3314
+ const value = s.value ?? {};
3315
+ return {
3316
+ surfaceBackground: s.surfaceBackground ?? STAT_CARD_TOKENS.surfaceBackground,
3317
+ border: s.border ?? STAT_CARD_TOKENS.border,
3318
+ borderRadius: s.borderRadius ?? STAT_CARD_TOKENS.borderRadius,
3319
+ padding: s.padding ?? STAT_CARD_TOKENS.padding,
3320
+ gap: s.gap ?? STAT_CARD_TOKENS.gap,
3321
+ iconWellSize: well.size ?? STAT_CARD_TOKENS.iconWellSize,
3322
+ iconWellBorderRadius: well.borderRadius ?? STAT_CARD_TOKENS.iconWellBorderRadius,
3323
+ iconWellAccentMixPercent: String(
3324
+ well.accentMixPercent ?? STAT_CARD_TOKENS.iconWellAccentMixPercent
3325
+ ),
3326
+ labelFontSize: label.fontSize ?? STAT_CARD_TOKENS.labelFontSize,
3327
+ labelFontWeight: label.fontWeight ?? STAT_CARD_TOKENS.labelFontWeight,
3328
+ labelColor: label.color ?? STAT_CARD_TOKENS.labelColor,
3329
+ valueFontSize: value.fontSize ?? STAT_CARD_TOKENS.valueFontSize,
3330
+ valueFontWeight: value.fontWeight ?? STAT_CARD_TOKENS.valueFontWeight,
3331
+ valueColor: value.color ?? STAT_CARD_TOKENS.valueColor,
3332
+ };
3333
+ }
3334
+
3335
+ export interface StepperTokens {
3336
+ gap: string;
3337
+ stepGap: string;
3338
+ line: {
3339
+ width: string;
3340
+ height: string;
3341
+ completedBackground: string;
3342
+ pendingBackground: string;
3343
+ };
3344
+ indicator: {
3345
+ size: string;
3346
+ borderWidth: string;
3347
+ fontFamily: string;
3348
+ fontSize: string;
3349
+ fontWeight: string;
3350
+ lineHeight: string;
3351
+ completedBackground: string;
3352
+ completedBorder: string;
3353
+ completedColor: string;
3354
+ activeBackground: string;
3355
+ activeBorder: string;
3356
+ activeColor: string;
3357
+ pendingBackground: string;
3358
+ pendingBorder: string;
3359
+ pendingColor: string;
3360
+ };
3361
+ label: {
3362
+ fontFamily: string;
3363
+ fontSize: string;
3364
+ fontWeight: string;
3365
+ lineHeight: string;
3366
+ completedColor: string;
3367
+ activeColor: string;
3368
+ pendingColor: string;
3369
+ };
3370
+ }
3371
+
3372
+ const stepperComp = defaultResolved.components?.stepper;
3373
+ export const STEPPER_TOKENS: StepperTokens = {
3374
+ gap: stepperComp?.gap ?? '8px',
3375
+ stepGap: stepperComp?.stepGap ?? '8px',
3376
+ line: {
3377
+ width: stepperComp?.line?.width ?? '40px',
3378
+ height: stepperComp?.line?.height ?? '2px',
3379
+ completedBackground: stepperComp?.line?.completedBackground ?? '#198754',
3380
+ pendingBackground: stepperComp?.line?.pendingBackground ?? '#D1D5DB',
3381
+ },
3382
+ indicator: {
3383
+ size: stepperComp?.indicator?.size ?? '34px',
3384
+ borderWidth: stepperComp?.indicator?.borderWidth ?? '2px',
3385
+ fontFamily: stepperComp?.indicator?.fontFamily ?? '"Inter", sans-serif',
3386
+ fontSize: stepperComp?.indicator?.fontSize ?? '16px',
3387
+ fontWeight: String(stepperComp?.indicator?.fontWeight ?? '600'),
3388
+ lineHeight: stepperComp?.indicator?.lineHeight ?? '24px',
3389
+ completedBackground: stepperComp?.indicator?.completedBackground ?? '#198754',
3390
+ completedBorder: stepperComp?.indicator?.completedBorder ?? '#198754',
3391
+ completedColor: stepperComp?.indicator?.completedColor ?? '#ffffff',
3392
+ activeBackground: stepperComp?.indicator?.activeBackground ?? '#e8f5ec',
3393
+ activeBorder: stepperComp?.indicator?.activeBorder ?? '#198754',
3394
+ activeColor: stepperComp?.indicator?.activeColor ?? '#2e3f2d',
3395
+ pendingBackground: stepperComp?.indicator?.pendingBackground ?? '#ffffff',
3396
+ pendingBorder: stepperComp?.indicator?.pendingBorder ?? '#D1D5DB',
3397
+ pendingColor: stepperComp?.indicator?.pendingColor ?? '#9CA3AF',
3398
+ },
3399
+ label: {
3400
+ fontFamily: stepperComp?.label?.fontFamily ?? '"Inter", sans-serif',
3401
+ fontSize: stepperComp?.label?.fontSize ?? '32px',
3402
+ fontWeight: String(stepperComp?.label?.fontWeight ?? '600'),
3403
+ lineHeight: stepperComp?.label?.lineHeight ?? '24px',
3404
+ completedColor: stepperComp?.label?.completedColor ?? '#2e3f2d',
3405
+ activeColor: stepperComp?.label?.activeColor ?? '#2e3f2d',
3406
+ pendingColor: stepperComp?.label?.pendingColor ?? '#9CA3AF',
3407
+ },
3408
+ };
3409
+
3410
+ export function getStepperTokensFromResolved(resolved: ResolvedTokens): StepperTokens {
3411
+ const s = resolved.components?.stepper as Record<string, any> | undefined;
3412
+ if (!s) return STEPPER_TOKENS;
3413
+
3414
+ return {
3415
+ gap: s.gap ?? STEPPER_TOKENS.gap,
3416
+ stepGap: s.stepGap ?? STEPPER_TOKENS.stepGap,
3417
+ line: {
3418
+ width: s.line?.width ?? STEPPER_TOKENS.line.width,
3419
+ height: s.line?.height ?? STEPPER_TOKENS.line.height,
3420
+ completedBackground: s.line?.completedBackground ?? STEPPER_TOKENS.line.completedBackground,
3421
+ pendingBackground: s.line?.pendingBackground ?? STEPPER_TOKENS.line.pendingBackground,
3422
+ },
3423
+ indicator: {
3424
+ size: s.indicator?.size ?? STEPPER_TOKENS.indicator.size,
3425
+ borderWidth: s.indicator?.borderWidth ?? STEPPER_TOKENS.indicator.borderWidth,
3426
+ fontFamily: s.indicator?.fontFamily ?? STEPPER_TOKENS.indicator.fontFamily,
3427
+ fontSize: s.indicator?.fontSize ?? STEPPER_TOKENS.indicator.fontSize,
3428
+ fontWeight: String(s.indicator?.fontWeight ?? STEPPER_TOKENS.indicator.fontWeight),
3429
+ lineHeight: s.indicator?.lineHeight ?? STEPPER_TOKENS.indicator.lineHeight,
3430
+ completedBackground:
3431
+ s.indicator?.completedBackground ?? STEPPER_TOKENS.indicator.completedBackground,
3432
+ completedBorder: s.indicator?.completedBorder ?? STEPPER_TOKENS.indicator.completedBorder,
3433
+ completedColor: s.indicator?.completedColor ?? STEPPER_TOKENS.indicator.completedColor,
3434
+ activeBackground: s.indicator?.activeBackground ?? STEPPER_TOKENS.indicator.activeBackground,
3435
+ activeBorder: s.indicator?.activeBorder ?? STEPPER_TOKENS.indicator.activeBorder,
3436
+ activeColor: s.indicator?.activeColor ?? STEPPER_TOKENS.indicator.activeColor,
3437
+ pendingBackground:
3438
+ s.indicator?.pendingBackground ?? STEPPER_TOKENS.indicator.pendingBackground,
3439
+ pendingBorder: s.indicator?.pendingBorder ?? STEPPER_TOKENS.indicator.pendingBorder,
3440
+ pendingColor: s.indicator?.pendingColor ?? STEPPER_TOKENS.indicator.pendingColor,
3441
+ },
3442
+ label: {
3443
+ fontFamily: s.label?.fontFamily ?? STEPPER_TOKENS.label.fontFamily,
3444
+ fontSize: s.label?.fontSize ?? STEPPER_TOKENS.label.fontSize,
3445
+ fontWeight: String(s.label?.fontWeight ?? STEPPER_TOKENS.label.fontWeight),
3446
+ lineHeight: s.label?.lineHeight ?? STEPPER_TOKENS.label.lineHeight,
3447
+ completedColor: s.label?.completedColor ?? STEPPER_TOKENS.label.completedColor,
3448
+ activeColor: s.label?.activeColor ?? STEPPER_TOKENS.label.activeColor,
3449
+ pendingColor: s.label?.pendingColor ?? STEPPER_TOKENS.label.pendingColor,
3450
+ },
3451
+ };
3452
+ }
3453
+
3454
+ export interface AccordionTokens {
3455
+ container: {
3456
+ width: string;
3457
+ borderWidth: string;
3458
+ borderColor: string;
3459
+ borderRadius: string;
3460
+ background: string;
3461
+ };
3462
+ header: {
3463
+ padding: string;
3464
+ gap: string;
3465
+ contentGap: string;
3466
+ defaultBackground: string;
3467
+ hoverBackground: string;
3468
+ expandedBackground: string;
3469
+ };
3470
+ statusIcon: {
3471
+ size: string;
3472
+ background: string;
3473
+ color: string;
3474
+ symbol: string;
3475
+ fontSize: string;
3476
+ fontWeight: string;
3477
+ lineHeight: string;
3478
+ };
3479
+ title: {
3480
+ fontFamily: string;
3481
+ fontSize: string;
3482
+ fontWeight: string;
3483
+ lineHeight: string;
3484
+ color: string;
3485
+ };
3486
+ subtitle: {
3487
+ fontFamily: string;
3488
+ fontSize: string;
3489
+ fontStyle: string;
3490
+ fontWeight: string;
3491
+ lineHeight: string;
3492
+ color: string;
3493
+ };
3494
+ actionIcon: {
3495
+ size: string;
3496
+ color: string;
3497
+ strokeWidth: string;
3498
+ };
3499
+ content: {
3500
+ padding: string;
3501
+ background: string;
3502
+ };
3503
+ }
3504
+
3505
+ const accordionComp = defaultResolved.components?.accordion;
3506
+ export const ACCORDION_TOKENS: AccordionTokens = {
3507
+ container: {
3508
+ width: accordionComp?.container?.width ?? '100%',
3509
+ borderWidth: accordionComp?.container?.borderWidth ?? '1px',
3510
+ borderColor: accordionComp?.container?.borderColor ?? '#D4DDE1',
3511
+ borderRadius: accordionComp?.container?.borderRadius ?? '12px',
3512
+ background: accordionComp?.container?.background ?? '#ffffff',
3513
+ },
3514
+ header: {
3515
+ padding: accordionComp?.header?.padding ?? '20px',
3516
+ gap: accordionComp?.header?.gap ?? '16px',
3517
+ contentGap: accordionComp?.header?.contentGap ?? '16px',
3518
+ defaultBackground: accordionComp?.header?.defaultBackground ?? '#f5f7f8',
3519
+ hoverBackground: accordionComp?.header?.hoverBackground ?? '#ffffff',
3520
+ expandedBackground: accordionComp?.header?.expandedBackground ?? '#e7f2d4',
3521
+ },
3522
+ statusIcon: {
3523
+ size: accordionComp?.statusIcon?.size ?? '40px',
3524
+ background: accordionComp?.statusIcon?.background ?? '#6B8F00',
3525
+ color: accordionComp?.statusIcon?.color ?? '#ffffff',
3526
+ symbol: accordionComp?.statusIcon?.symbol ?? '✓',
3527
+ fontSize: accordionComp?.statusIcon?.fontSize ?? '20px',
3528
+ fontWeight: String(accordionComp?.statusIcon?.fontWeight ?? '700'),
3529
+ lineHeight: accordionComp?.statusIcon?.lineHeight ?? '20px',
3530
+ },
3531
+ title: {
3532
+ fontFamily: accordionComp?.title?.fontFamily ?? '"Inter", sans-serif',
3533
+ fontSize: accordionComp?.title?.fontSize ?? '34px',
3534
+ fontWeight: String(accordionComp?.title?.fontWeight ?? '600'),
3535
+ lineHeight: accordionComp?.title?.lineHeight ?? '40px',
3536
+ color: accordionComp?.title?.color ?? '#22313A',
3537
+ },
3538
+ subtitle: {
3539
+ fontFamily: accordionComp?.subtitle?.fontFamily ?? '"Inter", sans-serif',
3540
+ fontSize: accordionComp?.subtitle?.fontSize ?? '30px',
3541
+ fontStyle: accordionComp?.subtitle?.fontStyle ?? 'italic',
3542
+ fontWeight: String(accordionComp?.subtitle?.fontWeight ?? '500'),
3543
+ lineHeight: accordionComp?.subtitle?.lineHeight ?? '32px',
3544
+ color: accordionComp?.subtitle?.color ?? '#56636A',
3545
+ },
3546
+ actionIcon: {
3547
+ size: accordionComp?.actionIcon?.size ?? '24px',
3548
+ color: accordionComp?.actionIcon?.color ?? '#6B8F00',
3549
+ strokeWidth: accordionComp?.actionIcon?.strokeWidth ?? '2px',
3550
+ },
3551
+ content: {
3552
+ padding: accordionComp?.content?.padding ?? '20px',
3553
+ background: accordionComp?.content?.background ?? '#ffffff',
3554
+ },
3555
+ };
3556
+
3557
+ export function getAccordionTokensFromResolved(resolved: ResolvedTokens): AccordionTokens {
3558
+ const a = resolved.components?.accordion as Record<string, any> | undefined;
3559
+ if (!a) return ACCORDION_TOKENS;
3560
+
3561
+ return {
3562
+ container: {
3563
+ width: a.container?.width ?? ACCORDION_TOKENS.container.width,
3564
+ borderWidth: a.container?.borderWidth ?? ACCORDION_TOKENS.container.borderWidth,
3565
+ borderColor: a.container?.borderColor ?? ACCORDION_TOKENS.container.borderColor,
3566
+ borderRadius: a.container?.borderRadius ?? ACCORDION_TOKENS.container.borderRadius,
3567
+ background: a.container?.background ?? ACCORDION_TOKENS.container.background,
3568
+ },
3569
+ header: {
3570
+ padding: a.header?.padding ?? ACCORDION_TOKENS.header.padding,
3571
+ gap: a.header?.gap ?? ACCORDION_TOKENS.header.gap,
3572
+ contentGap: a.header?.contentGap ?? ACCORDION_TOKENS.header.contentGap,
3573
+ defaultBackground: a.header?.defaultBackground ?? ACCORDION_TOKENS.header.defaultBackground,
3574
+ hoverBackground: a.header?.hoverBackground ?? ACCORDION_TOKENS.header.hoverBackground,
3575
+ expandedBackground:
3576
+ a.header?.expandedBackground ?? ACCORDION_TOKENS.header.expandedBackground,
3577
+ },
3578
+ statusIcon: {
3579
+ size: a.statusIcon?.size ?? ACCORDION_TOKENS.statusIcon.size,
3580
+ background: a.statusIcon?.background ?? ACCORDION_TOKENS.statusIcon.background,
3581
+ color: a.statusIcon?.color ?? ACCORDION_TOKENS.statusIcon.color,
3582
+ symbol: a.statusIcon?.symbol ?? ACCORDION_TOKENS.statusIcon.symbol,
3583
+ fontSize: a.statusIcon?.fontSize ?? ACCORDION_TOKENS.statusIcon.fontSize,
3584
+ fontWeight: String(a.statusIcon?.fontWeight ?? ACCORDION_TOKENS.statusIcon.fontWeight),
3585
+ lineHeight: a.statusIcon?.lineHeight ?? ACCORDION_TOKENS.statusIcon.lineHeight,
3586
+ },
3587
+ title: {
3588
+ fontFamily: a.title?.fontFamily ?? ACCORDION_TOKENS.title.fontFamily,
3589
+ fontSize: a.title?.fontSize ?? ACCORDION_TOKENS.title.fontSize,
3590
+ fontWeight: String(a.title?.fontWeight ?? ACCORDION_TOKENS.title.fontWeight),
3591
+ lineHeight: a.title?.lineHeight ?? ACCORDION_TOKENS.title.lineHeight,
3592
+ color: a.title?.color ?? ACCORDION_TOKENS.title.color,
3593
+ },
3594
+ subtitle: {
3595
+ fontFamily: a.subtitle?.fontFamily ?? ACCORDION_TOKENS.subtitle.fontFamily,
3596
+ fontSize: a.subtitle?.fontSize ?? ACCORDION_TOKENS.subtitle.fontSize,
3597
+ fontStyle: a.subtitle?.fontStyle ?? ACCORDION_TOKENS.subtitle.fontStyle,
3598
+ fontWeight: String(a.subtitle?.fontWeight ?? ACCORDION_TOKENS.subtitle.fontWeight),
3599
+ lineHeight: a.subtitle?.lineHeight ?? ACCORDION_TOKENS.subtitle.lineHeight,
3600
+ color: a.subtitle?.color ?? ACCORDION_TOKENS.subtitle.color,
3601
+ },
3602
+ actionIcon: {
3603
+ size: a.actionIcon?.size ?? ACCORDION_TOKENS.actionIcon.size,
3604
+ color: a.actionIcon?.color ?? ACCORDION_TOKENS.actionIcon.color,
3605
+ strokeWidth: a.actionIcon?.strokeWidth ?? ACCORDION_TOKENS.actionIcon.strokeWidth,
3606
+ },
3607
+ content: {
3608
+ padding: a.content?.padding ?? ACCORDION_TOKENS.content.padding,
3609
+ background: a.content?.background ?? ACCORDION_TOKENS.content.background,
3610
+ },
3611
+ };
3612
+ }
3613
+
3614
+ // ReportCard tokens
3615
+ export interface ReportCardTokens {
3616
+ headerBackground: string;
3617
+ headerColor: string;
3618
+ bodyBackground: string;
3619
+ dividerColor: string;
3620
+ bulletColor: string;
3621
+ itemColor: string;
3622
+ itemHoverColor: string;
3623
+ borderRadius: string;
3624
+ shadow: string;
3625
+ }
3626
+
3627
+ const reportCardComp = (components as any).reportcard as Record<string, any> | undefined;
3628
+ export const REPORT_CARD_TOKENS: ReportCardTokens = {
3629
+ headerBackground: reportCardComp?.header?.background ?? '#52760A',
3630
+ headerColor: reportCardComp?.header?.color ?? '#ffffff',
3631
+ bodyBackground: reportCardComp?.body?.background ?? '#ffffff',
3632
+ dividerColor: reportCardComp?.body?.dividerColor ?? '#E5E9EB',
3633
+ bulletColor: reportCardComp?.body?.bulletColor ?? '#D1D5DB',
3634
+ itemColor: reportCardComp?.body?.itemColor ?? '#4B5563',
3635
+ itemHoverColor: reportCardComp?.body?.itemHoverColor ?? '#52760A',
3636
+ borderRadius: reportCardComp?.borderRadius ?? '8px',
3637
+ shadow: reportCardComp?.shadow ?? '0 1px 4px rgba(0,0,0,0.08)',
3638
+ };
3639
+
3640
+ const defaultReportCardTokens = REPORT_CARD_TOKENS;
3641
+
3642
+ /** Build ReportCard tokens from resolved tokens (e.g. from useTokens()). Use this so tokens update when theme mode (light/dark) or brand changes. */
3643
+ export function getReportCardTokensFromResolved(resolved: ResolvedTokens): ReportCardTokens {
3644
+ const rc = (resolved.components as any)?.reportcard as Record<string, any> | undefined;
3645
+ if (!rc) return defaultReportCardTokens;
3646
+ return {
3647
+ headerBackground: rc.header?.background ?? defaultReportCardTokens.headerBackground,
3648
+ headerColor: rc.header?.color ?? defaultReportCardTokens.headerColor,
3649
+ bodyBackground: rc.body?.background ?? defaultReportCardTokens.bodyBackground,
3650
+ dividerColor: rc.body?.dividerColor ?? defaultReportCardTokens.dividerColor,
3651
+ bulletColor: rc.body?.bulletColor ?? defaultReportCardTokens.bulletColor,
3652
+ itemColor: rc.body?.itemColor ?? defaultReportCardTokens.itemColor,
3653
+ itemHoverColor: rc.body?.itemHoverColor ?? defaultReportCardTokens.itemHoverColor,
3654
+ borderRadius: rc.borderRadius ?? defaultReportCardTokens.borderRadius,
3655
+ shadow: rc.shadow ?? defaultReportCardTokens.shadow,
3656
+ };
3657
+ }
3658
+
3659
+ // Spacing (legacy format)
3660
+ export const spacing = {
3661
+ xs: foundation.spacing['1'],
3662
+ sm: foundation.spacing['2'],
3663
+ md: foundation.spacing['3'],
3664
+ lg: foundation.spacing['4'],
3665
+ };