@astryxdesign/theme-neutral 0.0.0-bootstrap.0 → 0.1.0-canary.08d4cf4

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.
@@ -0,0 +1,540 @@
1
+ import {
2
+ neutralIconRegistry
3
+ } from "./chunk-TC7YR7RU.mjs";
4
+
5
+ // src/neutralTheme.ts
6
+ import { defineTheme, defineSyntaxTheme } from "@astryxdesign/core/theme";
7
+ var neutralSyntax = defineSyntaxTheme({
8
+ name: "xds-neutral",
9
+ tokens: {
10
+ keyword: ["#700084", "#efa8ff"],
11
+ // purple T30/T80
12
+ string: ["#005600", "#a6d2a2"],
13
+ // green (sat T30 / pastel T80)
14
+ comment: ["#737373", "#a3a3a3"],
15
+ // neutral
16
+ number: ["#6e3500", "#ffb37f"],
17
+ // orange
18
+ function: ["#00458c", "#a0caff"],
19
+ // blue T30/T80 H=255
20
+ type: ["#700084", "#efa8ff"],
21
+ // purple
22
+ variable: ["#171717", "#e5e5e5"],
23
+ // near-black / near-white
24
+ operator: ["#737373", "#a3a3a3"],
25
+ // neutral
26
+ constant: ["#6e3500", "#ffb37f"],
27
+ // orange
28
+ tag: ["#89001a", "#ffaeaa"],
29
+ // red
30
+ attribute: ["#584400", "#eec12f"],
31
+ // yellow
32
+ property: ["#005348", "#83dac9"],
33
+ // teal
34
+ punctuation: ["#a3a3a3", "#525252"],
35
+ // neutral
36
+ background: ["#fafafa", "#0a0a0a"]
37
+ }
38
+ });
39
+ var neutralTheme = defineTheme({
40
+ name: "neutral",
41
+ // Typography: Figtree across body, heading, and display sizes (display
42
+ // size tokens inherit from heading.family). Monospace stays as the
43
+ // platform default for code.
44
+ // Scale: base=14, ratio=1.2. Bold weights on h3/h4 for subsection hierarchy.
45
+ typography: {
46
+ scale: { base: 14, ratio: 1.2 },
47
+ body: {
48
+ family: "Figtree",
49
+ fallbacks: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif'
50
+ },
51
+ heading: {
52
+ family: "Figtree",
53
+ fallbacks: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
54
+ weights: { 3: "bold", 4: "bold" }
55
+ },
56
+ code: {
57
+ family: "ui-monospace",
58
+ fallbacks: '"SF Mono", Monaco, Consolas, "Liberation Mono", "Courier New", monospace'
59
+ }
60
+ },
61
+ // Motion: snappier than default to match shadcn/Tailwind conventions.
62
+ // Produces: fast-min=95ms, fast=125ms, fast-max=165ms,
63
+ // medium-min=225ms, medium=300ms, medium-max=400ms.
64
+ motion: { fast: 125, medium: 300, slow: 700, ratio: 0.75 },
65
+ syntax: neutralSyntax,
66
+ tokens: {
67
+ // =========================================================================
68
+ // Core — pure grayscale spine (Tailwind neutral)
69
+ // 50:#fafafa 100:#f5f5f5 200:#e5e5e5 300:#d4d4d4 400:#a3a3a3
70
+ // 500:#737373 600:#525252 700:#404040 800:#262626 900:#171717 950:#0a0a0a
71
+ // =========================================================================
72
+ // =========================================================================
73
+ // Backgrounds — Figma-style flat with a single lifted surface.
74
+ //
75
+ // Dark mode collapses card / popover / muted to body T10. Cards and
76
+ // popovers lift purely via shadow + inset highlight (see --shadow-*
77
+ // below) — they don't need a distinct tone.
78
+ //
79
+ // Surface is the exception: it's tonally LIGHTER than body (T15) so
80
+ // interactive components that sit on top of body have a clear,
81
+ // differentiated foreground. Real consumers of --color-background-surface
82
+ // are: switches, radios, checkboxes, multi-selectors, dialogs, app
83
+ // shells, sections — all things that need to lift above the canvas.
84
+ //
85
+ // surface T15 #262626 — interactive surfaces lifted above body
86
+ // body T10 #1b1b1b — main canvas
87
+ // card T10 #1b1b1b — same as body, lifts via --shadow-low
88
+ // popover T10 #1b1b1b — same as body, lifts via --shadow-med
89
+ // muted T10 #1b1b1b — same as body
90
+ //
91
+ // Light mode keeps the standard ladder (white surfaces float on tinted
92
+ // body; shadows do most of the lifting):
93
+ // surface T100 #ffffff
94
+ // body T95 #f1f1f1
95
+ // card T100 #ffffff
96
+ // popover T100 #ffffff
97
+ // muted T95 #f1f1f1
98
+ //
99
+ // All values use the OKLCH Neutral tonal palette (chroma=0).
100
+ // =========================================================================
101
+ "--color-background-surface": ["#ffffff", "#262626"],
102
+ "--color-background-body": ["#f1f1f1", "#1b1b1b"],
103
+ "--color-background-card": ["#ffffff", "#1b1b1b"],
104
+ "--color-background-popover": ["#ffffff", "#1b1b1b"],
105
+ "--color-background-muted": ["#f1f1f1", "#1b1b1b"],
106
+ // Accent + neutral surface tints (sit alongside backgrounds)
107
+ "--color-accent": ["#262626", "#ebebeb"],
108
+ "--color-accent-muted": ["#f1f1f1", "#262626"],
109
+ "--color-neutral": ["#0000000F", "#FFFFFF1A"],
110
+ // Overlays (modal scrims, hover/pressed tints)
111
+ "--color-overlay": ["#00000080", "#000000CC"],
112
+ "--color-overlay-hover": ["#0000000D", "#FFFFFF0D"],
113
+ "--color-overlay-pressed": ["#0000001A", "#FFFFFF1A"],
114
+ // Text
115
+ "--color-text-primary": ["#171717", "#fafafa"],
116
+ "--color-text-secondary": ["#737373", "#a3a3a3"],
117
+ "--color-text-disabled": ["#a3a3a3", "#525252"],
118
+ "--color-text-accent": ["#262626", "#ebebeb"],
119
+ "--color-on-dark": "#ffffff",
120
+ "--color-on-light": "#171717",
121
+ // Contrast: neutral accent is near-black (L) / near-white (D)
122
+ "--color-on-accent": ["#ffffff", "#171717"],
123
+ "--color-on-success": ["#ffffff", "#171717"],
124
+ "--color-on-error": ["#ffffff", "#171717"],
125
+ "--color-on-warning": "#171717",
126
+ // Icon
127
+ "--color-icon-accent": ["#262626", "#ebebeb"],
128
+ "--color-icon-primary": ["#171717", "#fafafa"],
129
+ "--color-icon-secondary": ["#737373", "#a3a3a3"],
130
+ "--color-icon-disabled": ["#a3a3a3", "#525252"],
131
+ // Status / Sentiment — dark mode follows the issue #2150 rubric:
132
+ //
133
+ // Light mode: pastel T90 banner bg + dark T30/T40 text/icon. Locked
134
+ // light values for cards/banners/inputs/destructive btn.
135
+ // Dark mode : tinted-dark T20 bg + light pastel T80 text. INVERTED
136
+ // from light. Avoids the §5 "pastel-in-both-modes"
137
+ // anti-pattern (locked pastels glow against a dark body).
138
+ //
139
+ // --color-X = "saturated text/icon stop":
140
+ // light = T40 dark colored (sits on light pastel)
141
+ // dark = T80 light pastel (sits on dark tinted bg)
142
+ // Used by destructive button text, input border/icon
143
+ // (in light), banner-status-* text overrides.
144
+ // --color-X-muted = "muted bg stop":
145
+ // light = T90 light pastel
146
+ // dark = hue-tinted alpha overlay (T70 stop @ 24%)
147
+ // Used by banner bg, status-input message bg,
148
+ // destructive button bg. Dark mode uses an alpha
149
+ // overlay rather than a solid T20 tinted bg so
150
+ // the surface composes onto whatever sits behind
151
+ // it (body, card, popover) rather than reading
152
+ // as a hard colored panel.
153
+ //
154
+ // 24% alpha = '3D' suffix. Hue values match --color-icon-{X} dark
155
+ // slots (palette T70). Composited onto body #1b1b1b, the effective
156
+ // bg luminance hits ~1.65-1.70:1 vs body — visible colored surface
157
+ // without the heaviness of a solid T20 panel.
158
+ "--color-success": ["#007004", "#9fe59b"],
159
+ "--color-error": ["#a50c25", "#ffc6c1"],
160
+ "--color-warning": ["#745b00", "#fdcf4f"],
161
+ "--color-success-muted": ["#c5e5c0", "#84c9803D"],
162
+ "--color-error-muted": ["#facecb", "#ff9e973D"],
163
+ "--color-warning-muted": ["#f8da9d", "#deb4333D"],
164
+ // Border
165
+ "--color-border": ["#ebebeb", "#FFFFFF1A"],
166
+ "--color-border-emphasized": ["#d4d4d4", "#525252"],
167
+ // Effects
168
+ "--color-skeleton": ["#ebebeb", "#525252"],
169
+ "--color-shadow": ["#0000001A", "#0000004D"],
170
+ "--color-tint-hover": ["black", "white"],
171
+ // =========================================================================
172
+ // Categorical — light mode uses pastel surfaces + dark colored text;
173
+ // dark mode INVERTS to a hue-tinted alpha overlay surface +
174
+ // light pastel text (per #2150 rubric §3 — pick the tone
175
+ // that satisfies required contrast against every surface
176
+ // the token touches).
177
+ //
178
+ // Per-token tone choice (CIELab L*):
179
+ // bg light=T87-T90 pastel dark=T70 hue @ 24% alpha overlay
180
+ // (composites onto body to ~1.65:1
181
+ // vs body — colored surface that
182
+ // feels lighter than a solid T20
183
+ // panel; same hue as --color-icon-X
184
+ // dark slot, just at lower opacity)
185
+ // border light=T80 pastel dark=T60 mid-bright (>=5.8:1 vs body)
186
+ // icon light=T30 dark colored dark=T70 light pastel
187
+ // text light=T30 dark colored dark=T80 light pastel (>=7:1 on bg)
188
+ //
189
+ // Light pastels still use the per-hue chroma table (red/blue C=0.05,
190
+ // orange/green/purple/pink C=0.06, teal/cyan C=0.07, yellow H=85 C=0.10)
191
+ // for equal PERCEIVED saturation. Dark stops (T60/T70/T80) come from
192
+ // the dark-mode tonal palette (chroma×0.85, +5 tone lift tapering 80-95).
193
+ // =========================================================================
194
+ // Each row's dark slots are HCT-derived from the source hex listed in
195
+ // apps/sandbox/src/app/(fullscreen)/pages/neutral-palette/page.tsx via
196
+ // the canonical dark-ramp transform (chroma×0.85, +5 tone-lift taper)
197
+ // — same algorithm the Tonal Palettes preview renders. Border=T60,
198
+ // icon=T70, text=T80. Background uses the T70 hue at 24% alpha so the
199
+ // overlay surface composites onto body to ~1.65:1 luminance.
200
+ // Red H=22 — source #eb183a
201
+ "--color-background-red": ["#facecb", "#ff9e973D"],
202
+ "--color-border-red": ["#e6bab8", "#ff6f6c"],
203
+ "--color-icon-red": ["#89001a", "#ff9e97"],
204
+ "--color-text-red": ["#89001a", "#ffc6c1"],
205
+ // Orange H=55 — source #d57113
206
+ "--color-background-orange": ["#fad0b5", "#ffa2583D"],
207
+ "--color-border-orange": ["#e6bda2", "#e2883e"],
208
+ "--color-icon-orange": ["#6e3500", "#ffa258"],
209
+ "--color-text-orange": ["#6e3500", "#ffc9a2"],
210
+ // Yellow H=90 — source #f8c723
211
+ // Light-mode butter-yellow pastel at H=85 C=0.085 L=0.90 — yellow
212
+ // sits at the green-cyan luminance peak so it feels louder than the
213
+ // other status hues at the same canonical L. Picker decision: pull
214
+ // L down one step (0.91→0.90) and C down to its identity floor
215
+ // (0.10→0.085, just above the bronze threshold) so it sits closer
216
+ // to red/blue's perceived brightness without losing yellow identity.
217
+ // Dark-mode comes from the canonical H=90 ramp for tonal-palette
218
+ // consistency.
219
+ "--color-background-yellow": ["#f8da9d", "#deb4333D"],
220
+ "--color-border-yellow": ["#e4c279", "#c0990e"],
221
+ "--color-icon-yellow": ["#584400", "#deb433"],
222
+ "--color-text-yellow": ["#584400", "#fdcf4f"],
223
+ // Green H=144 — source #358a3a
224
+ "--color-background-green": ["#c5e5c0", "#84c9803D"],
225
+ "--color-border-green": ["#b2d1ac", "#69ad67"],
226
+ "--color-icon-green": ["#0c5700", "#84c980"],
227
+ "--color-text-green": ["#0c5700", "#9fe59b"],
228
+ // Teal H=180 — source #0c7365
229
+ // Light pastel uses L=0.87 C=0.065 (a step darker + less chroma than
230
+ // the L=0.888 C=0.07 used by other hues) to compensate for the
231
+ // green-cyan luminance overshoot — at the same OKLCH L, teal/cyan read
232
+ // ~5% brighter than red/blue because the eye's luminance response
233
+ // peaks in this band. Dropping L+C brings perceived brightness in
234
+ // line with the rest of the palette without losing hue identity.
235
+ "--color-background-teal": ["#a5e3d6", "#7ec6b83D"],
236
+ "--color-border-teal": ["#94d6c8", "#63ab9d"],
237
+ "--color-icon-teal": ["#005348", "#7ec6b8"],
238
+ "--color-text-teal": ["#005348", "#99e2d3"],
239
+ // Cyan H=215 — source #0c6f82
240
+ // Same L=0.87 C=0.065 pastel as teal (luminance overshoot compensation).
241
+ "--color-background-cyan": ["#a3e0ef", "#83c2d43D"],
242
+ "--color-border-cyan": ["#91d3e3", "#67a7b8"],
243
+ "--color-icon-cyan": ["#00505f", "#83c2d4"],
244
+ "--color-text-cyan": ["#00505f", "#9edef0"],
245
+ // Blue H=255 — source #0074e2
246
+ // T50 #0074e2 reserved for filled Info badge / progressbar / inset hover.
247
+ "--color-background-blue": ["#c4ddfb", "#9eb7ff3D"],
248
+ "--color-border-blue": ["#b1c9e7", "#6d9cfe"],
249
+ "--color-icon-blue": ["#00458c", "#9eb7ff"],
250
+ "--color-text-blue": ["#00458c", "#c7d3ff"],
251
+ // Purple H=320 — source #980fb2
252
+ "--color-background-purple": ["#eccef3", "#f297ff3D"],
253
+ "--color-border-purple": ["#d8bbdf", "#dd74f0"],
254
+ "--color-icon-purple": ["#700084", "#f297ff"],
255
+ "--color-text-purple": ["#700084", "#fac1ff"],
256
+ // Pink H=355 — source #b10e69
257
+ "--color-background-pink": ["#fccadc", "#ff99c33D"],
258
+ "--color-border-pink": ["#e7b7c8", "#f273aa"],
259
+ "--color-icon-pink": ["#83004b", "#ff99c3"],
260
+ "--color-text-pink": ["#83004b", "#ffc3da"],
261
+ // Gray (categorical neutral, chroma 0)
262
+ // Light: #e5e5e5 (Neutral 200) so it's visibly distinct from the
263
+ // lighter body / muted surface (both #f5f5f5).
264
+ // Dark : var(--color-neutral) — semi-transparent white wash
265
+ // (#FFFFFF1A, 10%). Matches the same treatment the gray
266
+ // badge uses; clearly distinct from the body T10 #1b1b1b
267
+ // while staying chroma-0 neutral. Solid T15 #1c1c1c was
268
+ // indistinguishable from --color-background-muted.
269
+ "--color-background-gray": ["#e5e5e5", "var(--color-neutral)"],
270
+ "--color-border-gray": ["#d4d4d4", "#262626"],
271
+ "--color-icon-gray": ["#525252", "#a3a3a3"],
272
+ "--color-text-gray": ["#262626", "#e5e5e5"],
273
+ // =========================================================================
274
+ // Radius — slightly larger than default (kept as-is)
275
+ // =========================================================================
276
+ "--radius-none": "0.25rem",
277
+ "--radius-inner": "0.375rem",
278
+ "--radius-element": "0.625rem",
279
+ "--radius-container": "0.75rem",
280
+ "--radius-page": "1.75rem",
281
+ "--radius-full": "9999px",
282
+ // =========================================================================
283
+ // Shadows
284
+ //
285
+ // Light mode: matches origin/main exactly (5%/10% low+med, 10%/15% high).
286
+ // Subtle drops; light surfaces don't need rim highlights.
287
+ //
288
+ // Dark mode: deepened drops + an all-around 1px white inset that wraps
289
+ // every edge ("Figma-style bezel"). The inset mimics ambient light
290
+ // catching the surface's rim on every side, giving cards/popovers/modals
291
+ // a substantial "lit from above" feel that drop shadows alone can't
292
+ // achieve against a dark canvas.
293
+ // low : drops 25%/40% + 8% white all-around inset
294
+ // med : drops 35%/50% + 12% white all-around inset
295
+ // high : drops 50%/70% + 15% white all-around inset
296
+ //
297
+ // The inset layer uses light-dark(transparent, ...) so light mode is
298
+ // unaffected — main's exact light values are preserved.
299
+ // =========================================================================
300
+ "--shadow-low": "0 2px 4px light-dark(oklch(0 0 0 / 5%), oklch(0 0 0 / 25%)), 0 4px 8px light-dark(oklch(0 0 0 / 10%), oklch(0 0 0 / 40%)), inset 0 0 0 1px light-dark(transparent, oklch(1 0 0 / 8%))",
301
+ "--shadow-med": "0 2px 4px light-dark(oklch(0 0 0 / 5%), oklch(0 0 0 / 35%)), 0 4px 12px light-dark(oklch(0 0 0 / 10%), oklch(0 0 0 / 50%)), inset 0 0 0 1px light-dark(transparent, oklch(1 0 0 / 12%))",
302
+ "--shadow-high": "0 4px 6px light-dark(oklch(0 0 0 / 10%), oklch(0 0 0 / 50%)), 0 12px 24px light-dark(oklch(0 0 0 / 15%), oklch(0 0 0 / 70%)), inset 0 0 0 1px light-dark(transparent, oklch(1 0 0 / 15%))",
303
+ "--shadow-inset-hover": "inset 0px 0px 0px 2px #0074e24D",
304
+ "--shadow-inset-selected": "inset 0px 0px 0px 2px #0074e280",
305
+ "--shadow-inset-success": "inset 0px 0px 0px 2px #1981004D",
306
+ "--shadow-inset-warning": "inset 0px 0px 0px 2px #ffce2f4D",
307
+ "--shadow-inset-error": "inset 0px 0px 0px 2px #e33f4a4D"
308
+ },
309
+ components: {
310
+ // =========================================================================
311
+ // Button — primary gets white text, secondary gets a border, destructive
312
+ // uses the OKLCH red filled treatment.
313
+ // =========================================================================
314
+ button: {
315
+ "variant:destructive": {
316
+ backgroundColor: "var(--color-error-muted)",
317
+ // locked pastel red bg
318
+ color: "var(--color-error)"
319
+ // locked T30 red — matches banner/input error text
320
+ }
321
+ },
322
+ // =========================================================================
323
+ // Badge —
324
+ // Semantic (info/success/warning/error): filled saturated T50 + contrasting
325
+ // text (white, or dark on yellow). The filled-button rule from #2150
326
+ // §3 — text contrast locks the bg tone, so this stays at T50 in
327
+ // BOTH modes, unlike pastel surfaces which invert by mode.
328
+ // Categorical (blue/green/red/orange/etc.): pastel-tinted hue surface +
329
+ // colored text — light mode = soft T87-T90 + dark T30 text; dark mode
330
+ // = T20 tinted + T80 light pastel text (sources: --color-background-X
331
+ // and --color-text-X tokens).
332
+ // Neutral: light gray bg + dark text (or inverted in dark mode).
333
+ // =========================================================================
334
+ badge: {
335
+ // Semantic — filled saturated bg + contrasting text.
336
+ // Light: vivid T45-T55 from the OKLCH palette + white text
337
+ // (~4.5-5:1 — Material/Linear/Vercel pop).
338
+ // Dark : T60 stop from the dark-mode tonal palette (chroma×0.85,
339
+ // +5 tone-lift taper from issue #2150 §4) + DARK text.
340
+ // T60+white fails AA-large (~2.7:1); T60+dark hits 6.6-7:1
341
+ // and tames the §4 vibration. Same dark-text-on-bright-bg
342
+ // treatment that warning yellow uses in both modes.
343
+ "variant:info": {
344
+ // Light: T50 #0074e2 (palette saturated stop)
345
+ // Dark : T60 stop from dark-mode tonal palette of source #0074e2
346
+ backgroundColor: "light-dark(#0074e2, #6d9cfe)",
347
+ color: "light-dark(#ffffff, #171717)"
348
+ },
349
+ "variant:neutral": {
350
+ // Mirrors the gray categorical badge — same neutral chip treatment
351
+ // (Neutral 200 light / semi-transparent white wash dark) sourced
352
+ // from the gray hue tokens, so a single change at the token layer
353
+ // updates both variants.
354
+ backgroundColor: "var(--color-background-gray)",
355
+ color: "var(--color-text-gray)"
356
+ },
357
+ "variant:success": {
358
+ // Light: T45 #198100 (palette saturated stop)
359
+ // Dark : T60 stop from dark-mode tonal palette of source #198100
360
+ backgroundColor: "light-dark(#198100, #64af4c)",
361
+ color: "light-dark(#ffffff, #171717)"
362
+ },
363
+ "variant:warning": {
364
+ // Yellow stays at the same hex in both modes — chroma reduction
365
+ // is barely visible at T85, and dark text on yellow doesn't
366
+ // suffer from the §4 vibration concern.
367
+ backgroundColor: "#ffce2f",
368
+ color: "#171717"
369
+ },
370
+ "variant:error": {
371
+ // Light: T55 #e33f4a (palette saturated stop)
372
+ // Dark : T60 stop from dark-mode tonal palette of Tailwind red-600
373
+ // source #dc2626 (kept on H=27 alarm-red rather than coral)
374
+ backgroundColor: "light-dark(#e33f4a, #ff705d)",
375
+ color: "light-dark(#ffffff, #171717)"
376
+ },
377
+ // Categorical — bg + text reference the per-hue tokens, so behavior
378
+ // tracks the categorical palette automatically:
379
+ // Light: pastel T87-T90 bg + dark T30 colored text (low-key chip)
380
+ // Dark : tinted T20 bg + light T80 colored text (per #2150 §5,
381
+ // inverted from light to avoid the "pastel-in-both-modes"
382
+ // anti-pattern that makes locked light pastels glow on a
383
+ // dark body)
384
+ "variant:red": {
385
+ backgroundColor: "var(--color-background-red)",
386
+ color: "var(--color-text-red)"
387
+ },
388
+ "variant:orange": {
389
+ backgroundColor: "var(--color-background-orange)",
390
+ color: "var(--color-text-orange)"
391
+ },
392
+ "variant:yellow": {
393
+ backgroundColor: "var(--color-background-yellow)",
394
+ color: "var(--color-text-yellow)"
395
+ },
396
+ "variant:green": {
397
+ backgroundColor: "var(--color-background-green)",
398
+ color: "var(--color-text-green)"
399
+ },
400
+ "variant:teal": {
401
+ backgroundColor: "var(--color-background-teal)",
402
+ color: "var(--color-text-teal)"
403
+ },
404
+ "variant:cyan": {
405
+ backgroundColor: "var(--color-background-cyan)",
406
+ color: "var(--color-text-cyan)"
407
+ },
408
+ "variant:blue": {
409
+ backgroundColor: "var(--color-background-blue)",
410
+ color: "var(--color-text-blue)"
411
+ },
412
+ "variant:purple": {
413
+ backgroundColor: "var(--color-background-purple)",
414
+ color: "var(--color-text-purple)"
415
+ },
416
+ "variant:pink": {
417
+ backgroundColor: "var(--color-background-pink)",
418
+ color: "var(--color-text-pink)"
419
+ },
420
+ "variant:gray": {
421
+ backgroundColor: "var(--color-background-gray)",
422
+ color: "var(--color-text-gray)"
423
+ }
424
+ },
425
+ // =========================================================================
426
+ // Banner — sits on a hue-tinted surface with colored text/icon:
427
+ // Light: pastel T90 bg (pulled from --color-{X}-muted / --color-background-blue)
428
+ // + dark T30 colored text (--color-text-{hue}).
429
+ // Dark : tinted T20 bg (same tokens, dark slot) + light T80 colored text.
430
+ // Per #2150 §5 — large hue-tinted surfaces in dark mode invert
431
+ // to a deep tinted bg + light text rather than locking the
432
+ // light-mode pastel.
433
+ //
434
+ // The inner-header *-muted token is forced transparent so the outer
435
+ // tinted background shows through cleanly.
436
+ //
437
+ // Status overrides reference --color-text-{hue} so text/icon colors
438
+ // stay in sync with the palette anchors automatically.
439
+ banner: {
440
+ "status:info": {
441
+ backgroundColor: "var(--color-background-blue)",
442
+ "--color-accent-muted": "transparent",
443
+ "--color-text-primary": "var(--color-text-blue)",
444
+ "--color-text-secondary": "var(--color-text-blue)",
445
+ "--color-accent": "var(--color-text-blue)"
446
+ },
447
+ // success/warning/error banner bgs come from --color-{X}-muted, which
448
+ // already carries the correct light/dark tinted values. We only need
449
+ // to redirect the text/icon to the palette colored stop.
450
+ "status:success": {
451
+ "--color-text-primary": "var(--color-text-green)",
452
+ "--color-text-secondary": "var(--color-text-green)",
453
+ "--color-success": "var(--color-text-green)"
454
+ },
455
+ "status:warning": {
456
+ "--color-text-primary": "var(--color-text-yellow)",
457
+ "--color-text-secondary": "var(--color-text-yellow)",
458
+ "--color-warning": "var(--color-text-yellow)"
459
+ },
460
+ "status:error": {
461
+ "--color-text-primary": "var(--color-text-red)",
462
+ "--color-text-secondary": "var(--color-text-red)",
463
+ "--color-error": "var(--color-text-red)"
464
+ }
465
+ },
466
+ // =========================================================================
467
+ // TextInput — no per-status overrides needed. The global tokens
468
+ // --color-{success,error,warning} carry the correct values in both
469
+ // modes (light=T40 dark colored, dark=T80 light pastel) for both
470
+ // surfaces the input border/icon touches: the input surface
471
+ // (white/T15-dark) and the status message bubble (light pastel T90 /
472
+ // dark T20). Verified all six combinations clear AA non-text 3:1.
473
+ // =========================================================================
474
+ // =========================================================================
475
+ // Switch — off-state track uses the same lifted-neutral surface as the
476
+ // ProgressBar track (--color-border-emphasized). Aligns the two
477
+ // "channel-on-body" components so their off-states share one visual
478
+ // language: light T85 #d4d4d4 sits one step darker than the body T95
479
+ // bg, dark T35 #525252 sits one step lighter than the body T10. Each
480
+ // is a defined channel, not a wash that blends in.
481
+ // =========================================================================
482
+ switch: {
483
+ base: {
484
+ "--color-background-gray": "var(--color-border-emphasized)"
485
+ }
486
+ },
487
+ progressbar: {
488
+ base: {
489
+ // Track uses --color-background-muted; override it to
490
+ // --color-border-emphasized (Neutral T85 #d4d4d4 in light mode) so
491
+ // the track is clearly darker than the body bg (Neutral T95 #f1f1f1)
492
+ // and reads as a defined channel rather than blending in. Dark
493
+ // mode inherits T35 #525252 — same one-step-lighter behavior.
494
+ "--color-background-muted": "var(--color-border-emphasized)"
495
+ },
496
+ // Vivid stops match the filled semantic badge colors (info/success/
497
+ // warning/error variants in the badge override above). Same hex
498
+ // values; documented per role with palette provenance.
499
+ "variant:accent": {
500
+ // Blue T50 saturated stop (= variant:info badge bg)
501
+ "--color-accent": "#0074e2"
502
+ },
503
+ "variant:success": {
504
+ // Green T45 saturated stop (= variant:success badge bg)
505
+ "--color-success": "#198100"
506
+ },
507
+ "variant:warning": {
508
+ // Yellow T85 saturated stop (= variant:warning badge bg)
509
+ "--color-warning": "#ffce2f"
510
+ },
511
+ "variant:error": {
512
+ // Red T55 saturated stop (= variant:error badge bg)
513
+ "--color-error": "#e33f4a"
514
+ }
515
+ },
516
+ // =========================================================================
517
+ // Card — tighter padding via public card padding token
518
+ // =========================================================================
519
+ card: {
520
+ base: {
521
+ padding: "var(--spacing-3)"
522
+ }
523
+ },
524
+ // =========================================================================
525
+ // Section — tighter padding via public section padding token
526
+ // =========================================================================
527
+ section: {
528
+ base: {
529
+ padding: "var(--spacing-3)"
530
+ }
531
+ }
532
+ // Heading and text component overrides are auto-generated by typography.scale.
533
+ // h3/h4 bold weights come from typography.heading.weights above.
534
+ },
535
+ icons: neutralIconRegistry
536
+ });
537
+ export {
538
+ neutralIconRegistry,
539
+ neutralTheme
540
+ };