@opendata-ai/openchart-core 8.0.0-rc.9 → 8.1.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 (83) hide show
  1. package/README.md +1 -1
  2. package/dist/index.d.ts +403 -45
  3. package/dist/index.js +50 -18
  4. package/dist/index.js.map +1 -1
  5. package/dist/styles.css +1 -1
  6. package/dist/styles.css.map +1 -1
  7. package/package.json +1 -2
  8. package/schema/chart.schema.json +375 -69
  9. package/schema/table.schema.json +35 -1
  10. package/schema/vizspec.schema.json +992 -171
  11. package/src/accessibility/__tests__/alt-text.test.ts +0 -110
  12. package/src/accessibility/__tests__/aria.test.ts +0 -125
  13. package/src/accessibility/alt-text.ts +0 -155
  14. package/src/accessibility/aria.ts +0 -75
  15. package/src/accessibility/index.ts +0 -6
  16. package/src/colors/__tests__/colorblind.test.ts +0 -75
  17. package/src/colors/__tests__/contrast.test.ts +0 -119
  18. package/src/colors/__tests__/palettes.test.ts +0 -74
  19. package/src/colors/colorblind.ts +0 -131
  20. package/src/colors/contrast.ts +0 -143
  21. package/src/colors/index.ts +0 -36
  22. package/src/colors/palettes.ts +0 -206
  23. package/src/helpers/__tests__/deep-merge.test.ts +0 -54
  24. package/src/helpers/__tests__/spec-builders.test.ts +0 -338
  25. package/src/helpers/deep-merge.ts +0 -29
  26. package/src/helpers/spec-builders.ts +0 -479
  27. package/src/index.ts +0 -215
  28. package/src/labels/__tests__/collision.test.ts +0 -310
  29. package/src/labels/collision.ts +0 -249
  30. package/src/labels/index.ts +0 -13
  31. package/src/layout/__tests__/chrome.test.ts +0 -208
  32. package/src/layout/__tests__/text-measure.test.ts +0 -57
  33. package/src/layout/chrome.ts +0 -470
  34. package/src/layout/index.ts +0 -18
  35. package/src/layout/text-measure.ts +0 -404
  36. package/src/layout/text-wrap.ts +0 -93
  37. package/src/locale/__tests__/format.test.ts +0 -606
  38. package/src/locale/format.ts +0 -459
  39. package/src/locale/index.ts +0 -21
  40. package/src/responsive/__tests__/breakpoints.test.ts +0 -146
  41. package/src/responsive/__tests__/metrics.test.ts +0 -52
  42. package/src/responsive/breakpoints.ts +0 -177
  43. package/src/responsive/index.ts +0 -53
  44. package/src/responsive/metrics.ts +0 -299
  45. package/src/schema/__tests__/generated-artifacts.test.ts +0 -42
  46. package/src/schema/__tests__/schema.test.ts +0 -179
  47. package/src/styles/__tests__/reduced-motion-selectors.test.ts +0 -28
  48. package/src/styles/animation.css +0 -213
  49. package/src/styles/base.css +0 -102
  50. package/src/styles/chrome.css +0 -174
  51. package/src/styles/dark.css +0 -44
  52. package/src/styles/graph.css +0 -135
  53. package/src/styles/index.css +0 -28
  54. package/src/styles/keyframes.css +0 -151
  55. package/src/styles/legend.css +0 -18
  56. package/src/styles/reduced-motion.css +0 -67
  57. package/src/styles/series-search.css +0 -153
  58. package/src/styles/sparkline.css +0 -16
  59. package/src/styles/table-animation.css +0 -93
  60. package/src/styles/table.css +0 -398
  61. package/src/styles/token-definitions.ts +0 -199
  62. package/src/styles/tokens.css +0 -156
  63. package/src/styles/tooltip.css +0 -126
  64. package/src/styles/you-draw-it.css +0 -126
  65. package/src/theme/__tests__/dark-mode.test.ts +0 -232
  66. package/src/theme/__tests__/defaults.test.ts +0 -73
  67. package/src/theme/__tests__/resolve.test.ts +0 -277
  68. package/src/theme/dark-mode.ts +0 -258
  69. package/src/theme/defaults.ts +0 -110
  70. package/src/theme/index.ts +0 -8
  71. package/src/theme/presets.ts +0 -64
  72. package/src/theme/resolve.ts +0 -312
  73. package/src/types/__tests__/encoding.test.ts +0 -368
  74. package/src/types/__tests__/gradient.test.ts +0 -95
  75. package/src/types/__tests__/spec.test.ts +0 -499
  76. package/src/types/encoding.ts +0 -280
  77. package/src/types/events.ts +0 -236
  78. package/src/types/index.ts +0 -290
  79. package/src/types/layout.ts +0 -2024
  80. package/src/types/schema-roots.ts +0 -32
  81. package/src/types/spec.ts +0 -2925
  82. package/src/types/table.ts +0 -108
  83. package/src/types/theme.ts +0 -209
@@ -1,398 +0,0 @@
1
- @layer oc.components {
2
- /* ---------------------------------------------------------------------------
3
- * Table base
4
- * --------------------------------------------------------------------------- */
5
-
6
- .oc-table-wrapper {
7
- font-family: var(--oc-font-family);
8
- color: var(--oc-text);
9
- background: var(--oc-bg);
10
-
11
- & > .oc-chrome {
12
- margin-bottom: 16px;
13
- padding-left: 16px;
14
- padding-right: 16px;
15
- }
16
-
17
- & > .oc-chrome:first-child {
18
- padding-top: 4px;
19
- }
20
-
21
- & table {
22
- width: 100%;
23
- border-collapse: collapse;
24
- }
25
-
26
- & th,
27
- & td {
28
- padding: 10px 16px;
29
- text-align: left;
30
- border-bottom: 1px solid var(--oc-border);
31
- }
32
-
33
- & th {
34
- font-weight: 600;
35
- font-size: 12px;
36
- text-transform: uppercase;
37
- letter-spacing: 0.05em;
38
- color: var(--oc-text-secondary);
39
- white-space: nowrap;
40
- }
41
-
42
- & thead {
43
- position: sticky;
44
- top: 0;
45
- z-index: 2;
46
- background: var(--oc-bg);
47
- }
48
-
49
- & thead th {
50
- border-bottom-width: 2px;
51
- }
52
-
53
- & td {
54
- font-size: 14px;
55
- font-variant-numeric: tabular-nums;
56
- }
57
-
58
- & th:focus {
59
- outline: 2px solid var(--oc-focus);
60
- outline-offset: -2px;
61
- }
62
-
63
- & tbody:focus {
64
- outline: none;
65
- }
66
- }
67
-
68
- .oc-table-title {
69
- margin-bottom: 4px;
70
- font-size: var(--oc-title-computed-size, var(--oc-title-size));
71
- font-weight: var(--oc-title-computed-weight, var(--oc-title-weight));
72
- color: var(--oc-title-computed-color, var(--oc-text));
73
- }
74
-
75
- .oc-table-subtitle {
76
- margin-bottom: 8px;
77
- font-size: var(--oc-subtitle-computed-size, var(--oc-subtitle-size));
78
- font-weight: var(--oc-subtitle-computed-weight, var(--oc-subtitle-weight));
79
- color: var(--oc-subtitle-computed-color, var(--oc-text-secondary));
80
- }
81
-
82
- .oc-table-source {
83
- font-size: var(--oc-source-computed-size, var(--oc-source-size));
84
- color: var(--oc-source-computed-color, var(--oc-text-muted));
85
- }
86
-
87
- .oc-table-footer-text {
88
- font-size: var(--oc-footer-computed-size, var(--oc-source-size));
89
- color: var(--oc-footer-computed-color, var(--oc-text-muted));
90
- }
91
-
92
- .oc-table-scroll {
93
- overflow-x: auto;
94
- }
95
-
96
- /* ---------------------------------------------------------------------------
97
- * Sticky first column
98
- * --------------------------------------------------------------------------- */
99
-
100
- .oc-table--sticky {
101
- & th:first-child,
102
- & td:first-child {
103
- position: sticky;
104
- left: 0;
105
- z-index: 1;
106
- background: var(--oc-bg);
107
- }
108
- }
109
-
110
- /* ---------------------------------------------------------------------------
111
- * Sort controls
112
- * --------------------------------------------------------------------------- */
113
-
114
- .oc-table-sort-btn {
115
- background: none;
116
- border: none;
117
- cursor: pointer;
118
- padding: 2px;
119
- margin-left: 6px;
120
- display: inline-flex;
121
- flex-direction: column;
122
- align-items: center;
123
- vertical-align: middle;
124
- gap: 2px;
125
-
126
- &::before,
127
- &::after {
128
- content: "";
129
- display: block;
130
- width: 0;
131
- height: 0;
132
- border-left: 5px solid transparent;
133
- border-right: 5px solid transparent;
134
- transition:
135
- opacity 0.15s,
136
- border-color 0.15s;
137
- }
138
-
139
- &::before {
140
- border-bottom: 4.5px solid var(--oc-text-secondary);
141
- opacity: 0.45;
142
- }
143
-
144
- &::after {
145
- border-top: 4.5px solid var(--oc-text-secondary);
146
- opacity: 0.45;
147
- }
148
-
149
- &:hover::before,
150
- &:hover::after {
151
- opacity: 0.75;
152
- }
153
- }
154
-
155
- th[aria-sort="ascending"] .oc-table-sort-btn {
156
- &::before {
157
- opacity: 1;
158
- border-bottom-color: var(--oc-text);
159
- }
160
-
161
- &::after {
162
- opacity: 0.15;
163
- }
164
- }
165
-
166
- th[aria-sort="descending"] .oc-table-sort-btn {
167
- &::after {
168
- opacity: 1;
169
- border-top-color: var(--oc-text);
170
- }
171
-
172
- &::before {
173
- opacity: 0.15;
174
- }
175
- }
176
-
177
- /* ---------------------------------------------------------------------------
178
- * Search
179
- * --------------------------------------------------------------------------- */
180
-
181
- /* Shared search-input skin (table + series-search).
182
- Padding excluded: it diverges (table: 8px 12px; series: 0 10px 0 28px)
183
- and series-search.css is imported before table.css — a padding declaration
184
- here would beat the series-search override by source order. */
185
- .oc-table-search input,
186
- .oc-series-search-input {
187
- width: 100%;
188
- border: 1px solid var(--oc-border);
189
- border-radius: 6px;
190
- font-size: 13px;
191
- font-family: inherit;
192
- background: var(--oc-bg);
193
- color: var(--oc-text);
194
- box-sizing: border-box;
195
- transition: border-color 0.15s;
196
- }
197
-
198
- .oc-table-search input::placeholder,
199
- .oc-series-search-input::placeholder {
200
- color: var(--oc-text-muted);
201
- font-size: 13px;
202
- }
203
-
204
- .oc-table-search input:focus,
205
- .oc-series-search-input:focus {
206
- outline: none;
207
- border-color: var(--oc-focus);
208
- box-shadow: 0 0 0 3px var(--oc-focus-ring);
209
- }
210
-
211
- .oc-table-search {
212
- padding: 8px 0;
213
-
214
- & input {
215
- padding: 8px 12px;
216
- }
217
- }
218
-
219
- /* ---------------------------------------------------------------------------
220
- * Pagination
221
- * --------------------------------------------------------------------------- */
222
-
223
- .oc-table-pagination {
224
- display: flex;
225
- align-items: center;
226
- justify-content: space-between;
227
- padding: 12px 0 4px;
228
- font-size: 13px;
229
- color: var(--oc-text-secondary);
230
-
231
- & button {
232
- padding: 6px 14px;
233
- border: 1px solid var(--oc-border);
234
- border-radius: 6px;
235
- background: var(--oc-bg);
236
- color: var(--oc-text);
237
- cursor: pointer;
238
- font-size: 13px;
239
- font-family: inherit;
240
- transition:
241
- background 0.15s,
242
- border-color 0.15s;
243
-
244
- &:disabled {
245
- opacity: 0.35;
246
- cursor: not-allowed;
247
- }
248
-
249
- &:hover:not(:disabled) {
250
- background: var(--oc-hover-bg);
251
- border-color: var(--oc-axis);
252
- }
253
-
254
- &:focus-visible {
255
- outline: 2px solid var(--oc-focus);
256
- outline-offset: 1px;
257
- }
258
- }
259
- }
260
-
261
- .oc-table-pagination-info {
262
- font-variant-numeric: tabular-nums;
263
- }
264
-
265
- .oc-table-pagination-btns {
266
- display: flex;
267
- gap: 8px;
268
- }
269
-
270
- /* ---------------------------------------------------------------------------
271
- * Bar cells
272
- * --------------------------------------------------------------------------- */
273
-
274
- .oc-table-bar {
275
- position: relative;
276
- }
277
-
278
- .oc-table-bar-fill {
279
- position: absolute;
280
- top: 6px;
281
- left: 0;
282
- bottom: 6px;
283
- border-radius: 2px;
284
- opacity: 0.15;
285
- pointer-events: none;
286
- }
287
-
288
- .oc-table-bar-value {
289
- position: relative;
290
- z-index: 1;
291
- }
292
-
293
- /* ---------------------------------------------------------------------------
294
- * Sparkline cells
295
- * --------------------------------------------------------------------------- */
296
-
297
- .oc-table-sparkline {
298
- display: block;
299
- width: 100%;
300
- position: relative;
301
-
302
- & svg {
303
- display: block;
304
- width: 100%;
305
- overflow: visible;
306
- }
307
- }
308
-
309
- .oc-table-sparkline-dot {
310
- position: absolute;
311
- border-radius: 50%;
312
- width: 5px;
313
- height: 5px;
314
- }
315
-
316
- .oc-table-sparkline-labels {
317
- display: flex;
318
- justify-content: space-between;
319
- font-size: 11px;
320
- line-height: 1;
321
- }
322
-
323
- /* ---------------------------------------------------------------------------
324
- * Image cells
325
- * --------------------------------------------------------------------------- */
326
-
327
- .oc-table-image {
328
- display: inline-block;
329
- vertical-align: middle;
330
-
331
- & img {
332
- object-fit: cover;
333
- }
334
- }
335
-
336
- .oc-table-image-rounded img {
337
- border-radius: 50%;
338
- }
339
-
340
- /* ---------------------------------------------------------------------------
341
- * Flag cells
342
- * --------------------------------------------------------------------------- */
343
-
344
- .oc-table-flag {
345
- font-size: 1.2em;
346
- }
347
-
348
- /* ---------------------------------------------------------------------------
349
- * Compact mode
350
- * --------------------------------------------------------------------------- */
351
-
352
- .oc-table--compact {
353
- & th,
354
- & td {
355
- padding: 4px 8px;
356
- font-size: 13px;
357
- }
358
-
359
- & th {
360
- font-size: 11px;
361
- }
362
- }
363
-
364
- /* ---------------------------------------------------------------------------
365
- * Row hover (when onRowClick is set)
366
- * --------------------------------------------------------------------------- */
367
-
368
- .oc-table--clickable tbody {
369
- & tr {
370
- cursor: pointer;
371
- }
372
-
373
- & tr:hover {
374
- background: var(--oc-hover-bg);
375
- }
376
- }
377
-
378
- /* ---------------------------------------------------------------------------
379
- * Keyboard cell focus indicator
380
- * --------------------------------------------------------------------------- */
381
-
382
- .oc-table-cell-focus {
383
- outline: 2px solid var(--oc-focus);
384
- outline-offset: -2px;
385
- }
386
-
387
- /* ---------------------------------------------------------------------------
388
- * Empty state
389
- * --------------------------------------------------------------------------- */
390
-
391
- .oc-table-empty {
392
- padding: 32px 16px;
393
- text-align: center;
394
- color: var(--oc-text-secondary);
395
- font-size: 14px;
396
- font-style: italic;
397
- }
398
- }
@@ -1,199 +0,0 @@
1
- export interface CssTokenDef {
2
- name: string;
3
- light: string;
4
- dark?: string;
5
- comment?: string;
6
- consumers?: string;
7
- section?: string;
8
- }
9
-
10
- export const CSS_TOKEN_ROOT_SELECTORS: readonly string[] = [
11
- '.oc-root',
12
- '.oc-chart-root',
13
- '.oc-table-wrapper',
14
- '.oc-table-root',
15
- '.oc-graph-wrapper',
16
- '.oc-graph-root',
17
- '.oc-sankey-root',
18
- '.oc-tilemap-root',
19
- '.oc-barlist-root',
20
- '.oc-map-root',
21
- ];
22
-
23
- /**
24
- * Text levels. Names invert across modes: in light mode "muted" sits at
25
- * a lighter zinc step than "subtle" because both are picked relative to
26
- * the active background, not from a fixed lightness ladder. The intent
27
- * is "muted = first step away from primary text"; "subtle = next step
28
- * down"; etc.
29
- *
30
- * Light mode: text=zinc-950, secondary=zinc-700, muted=zinc-500,
31
- * subtle=zinc-400, faint=zinc-300
32
- * Dark mode (dark.css): inverts the surface tokens but keeps the same
33
- * muted -> subtle -> faint progression away from primary.
34
- */
35
-
36
- export const CSS_TOKENS: readonly CssTokenDef[] = [
37
- {
38
- name: '--oc-font-family',
39
- light:
40
- '\n "Inter Variable", Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
41
- },
42
- {
43
- name: '--oc-font-mono',
44
- light: '"JetBrains Mono", "Fira Code", "Cascadia Code", monospace',
45
- },
46
-
47
- {
48
- section: 'Animation easing presets via CSS linear()',
49
- name: '--oc-ease-smooth',
50
- light:
51
- 'linear(\n 0,\n 0.157,\n 0.438,\n 0.64,\n 0.766,\n 0.85,\n 0.906,\n 0.941,\n 0.964,\n 0.978,\n 0.988,\n 0.994,\n 0.998,\n 1\n )',
52
- },
53
- {
54
- name: '--oc-ease-snappy',
55
- light:
56
- 'linear(\n 0,\n 0.012,\n 0.048,\n 0.108,\n 0.194,\n 0.302,\n 0.426,\n 0.559,\n 0.69,\n 0.808,\n 0.905,\n 0.973,\n 1.013,\n 1.028,\n 1.023,\n 1.006,\n 0.984,\n 0.966,\n 0.957,\n 0.957,\n 0.964,\n 0.975,\n 0.986,\n 0.995,\n 1,\n 1.003,\n 1.002,\n 1,\n 0.998,\n 0.998,\n 0.999,\n 1\n )',
57
- consumers: 'EASE_VAR_MAP in vanilla renderers',
58
- },
59
-
60
- { section: 'Animation timing defaults', name: '--oc-animation-duration', light: '500ms' },
61
- { name: '--oc-animation-stagger', light: '80ms' },
62
- { name: '--oc-annotation-delay', light: '200ms' },
63
-
64
- { section: 'Typography scale (editorial design system)', name: '--oc-title-size', light: '26px' },
65
- { name: '--oc-title-weight', light: '590' },
66
- { name: '--oc-title-tracking', light: '-0.022em' },
67
- { name: '--oc-subtitle-size', light: '14px' },
68
- { name: '--oc-subtitle-weight', light: '450' },
69
- { name: '--oc-source-size', light: '11px' },
70
- { name: '--oc-source-weight', light: '450' },
71
- { name: '--oc-body-size', light: '13px' },
72
- { name: '--oc-eyebrow-size', light: '11px' },
73
- { name: '--oc-eyebrow-weight', light: '550' },
74
- { name: '--oc-eyebrow-tracking', light: '0.08em' },
75
-
76
- { section: 'Surfaces (light mode defaults)', name: '--oc-bg', light: '#ffffff', dark: '#09090b' },
77
- { name: '--oc-card', light: '#ffffff', dark: '#111113' },
78
- {
79
- name: '--oc-secondary',
80
- light: '#f4f4f5',
81
- dark: '#27272a',
82
- comment: 'zinc-100, raised surface',
83
- },
84
-
85
- {
86
- section: `Text levels. Names invert across modes: in light mode "muted" sits at
87
- * a lighter zinc step than "subtle" because both are picked relative to
88
- * the active background, not from a fixed lightness ladder. The intent
89
- * is "muted = first step away from primary text"; "subtle = next step
90
- * down"; etc.
91
- *
92
- * Light mode: text=zinc-950, secondary=zinc-700, muted=zinc-500,
93
- * subtle=zinc-400, faint=zinc-300
94
- * Dark mode (dark.css): inverts the surface tokens but keeps the same
95
- * muted -> subtle -> faint progression away from primary.`,
96
- name: '--oc-text',
97
- light: '#09090b',
98
- dark: '#f7f8f8',
99
- },
100
- { name: '--oc-text-secondary', light: '#3f3f46', dark: '#d0d6e0' },
101
- { name: '--oc-text-muted', light: '#71717a', dark: '#a1a1aa' },
102
- { name: '--oc-text-faint', light: '#d4d4d8', dark: '#52525b' },
103
-
104
- {
105
- section: 'Lines',
106
- name: '--oc-gridline',
107
- light: 'rgba(0, 0, 0, 0.1)',
108
- dark: 'rgba(255, 255, 255, 0.05)',
109
- },
110
- {
111
- name: '--oc-axis',
112
- light: 'rgba(0, 0, 0, 0.1)',
113
- dark: 'rgba(255, 255, 255, 0.1)',
114
- },
115
- {
116
- name: '--oc-border',
117
- light: 'rgba(0, 0, 0, 0.08)',
118
- dark: 'rgba(255, 255, 255, 0.1)',
119
- },
120
- { name: '--oc-border-radius', light: '2px' },
121
-
122
- {
123
- section: 'Brand and semantic',
124
- name: '--oc-accent',
125
- light: '#06b6d4',
126
- dark: '#06b6d4',
127
- },
128
- {
129
- name: '--oc-accent-strong',
130
- light: '#0891b2',
131
- dark: '#06b6d4',
132
- comment: 'darker cyan for line strokes on light bg',
133
- consumers: 'stamped via adaptForLightLineStroke',
134
- },
135
- { name: '--oc-positive', light: '#10b981', dark: '#34d399' },
136
- { name: '--oc-negative', light: '#e11d48', dark: '#fb7185' },
137
- { name: '--oc-focus', light: '#3b82f6', dark: '#60a5fa' },
138
- {
139
- name: '--oc-focus-ring',
140
- light: 'rgba(59, 130, 246, 0.1)',
141
- comment: 'static, intentionally NOT derived from --oc-focus and NOT flipped in dark mode',
142
- },
143
- {
144
- name: '--oc-focus-ring-strong',
145
- light: 'rgba(59, 130, 246, 0.25)',
146
- },
147
- {
148
- name: '--oc-editable-hover',
149
- light: 'rgba(79, 70, 229, 0.35)',
150
- comment: 'edit-mode hover outline, indigo one-off',
151
- },
152
-
153
- {
154
- section: 'Spacing scale (4px base) — only --oc-space-2/4 are consumed (JS-stamped)',
155
- name: '--oc-space-2',
156
- light: '8px',
157
- consumers:
158
- 'stamped at mount from theme.spacing (theme-tokens.ts); component CSS must NOT consume these',
159
- },
160
- {
161
- name: '--oc-space-4',
162
- light: '16px',
163
- consumers:
164
- 'stamped at mount from theme.spacing (theme-tokens.ts); component CSS must NOT consume these',
165
- },
166
-
167
- {
168
- section: 'Interactive states',
169
- name: '--oc-hover-bg',
170
- light: 'rgba(0, 0, 0, 0.025)',
171
- dark: 'rgba(255, 255, 255, 0.05)',
172
- },
173
- {
174
- name: '--oc-tooltip-bg',
175
- light: 'rgba(255, 255, 255, 0.88)',
176
- dark: 'rgba(17, 17, 19, 0.92)',
177
- },
178
- {
179
- name: '--oc-tooltip-border',
180
- light: 'rgba(0, 0, 0, 0.08)',
181
- dark: 'rgba(255, 255, 255, 0.08)',
182
- },
183
- {
184
- name: '--oc-tooltip-shadow',
185
- light: '0 2px 8px rgba(0, 0, 0, 0.08), 0 0 1px rgba(0, 0, 0, 0.12)',
186
- dark: '0 2px 8px rgba(0, 0, 0, 0.3), 0 0 1px rgba(0, 0, 0, 0.4)',
187
- },
188
- { name: '--oc-tooltip-text', light: '#09090b', dark: '#f7f8f8' },
189
- { name: '--oc-legend-text', light: '#3f3f46', dark: '#d0d6e0' },
190
- ];
191
-
192
- const TOKEN_MAP = new Map(CSS_TOKENS.map((t) => [t.name, t]));
193
-
194
- export function cssTokenDefault(name: string, mode: 'light' | 'dark'): string {
195
- const token = TOKEN_MAP.get(name);
196
- if (!token) throw new Error(`Unknown CSS token: ${name}`);
197
- if (mode === 'dark' && token.dark !== undefined) return token.dark;
198
- return token.light;
199
- }