@astryxdesign/theme-neutral 0.5.2-canary.457de8d → 0.5.2-canary.48cb3a4
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.
- package/dist/neutral.js +63 -16
- package/dist/neutral.variants.d.ts +7 -0
- package/dist/neutralTheme.d.ts.map +1 -1
- package/dist/neutralTheme.test.d.ts +2 -0
- package/dist/neutralTheme.test.d.ts.map +1 -0
- package/dist/source.js +55 -146
- package/dist/source.mjs +59 -147
- package/dist/theme.css +54 -16
- package/package.json +3 -3
- package/src/neutralTheme.test.ts +50 -0
- package/src/neutralTheme.ts +67 -147
package/dist/neutral.js
CHANGED
|
@@ -189,6 +189,25 @@ export const neutralTheme = {
|
|
|
189
189
|
"--shadow-inset-warning": "inset 0px 0px 0px 2px #ffce2f4D",
|
|
190
190
|
"--shadow-inset-error": "inset 0px 0px 0px 2px #e33f4a4D"
|
|
191
191
|
},
|
|
192
|
+
localTokens: {
|
|
193
|
+
"--astryx-theme-neutral-color-status-fill-accent": "light-dark(#0074e2, #6d9cfe)",
|
|
194
|
+
"--astryx-theme-neutral-color-status-fill-success": "light-dark(#198100, #64af4c)",
|
|
195
|
+
"--astryx-theme-neutral-color-status-fill-warning": "#ffce2f",
|
|
196
|
+
"--astryx-theme-neutral-color-status-fill-error": "light-dark(#c9303a, #ff705d)",
|
|
197
|
+
"--astryx-theme-neutral-color-on-tint-neutral": "light-dark(#0a0a0a4D, #fafafa4D)",
|
|
198
|
+
"--astryx-theme-neutral-color-on-tint-overlay-hover": "light-dark(#0a0a0a1A, #fafafa1A)",
|
|
199
|
+
"--astryx-theme-neutral-color-on-tint-overlay-pressed": "light-dark(#0a0a0a33, #fafafa33)"
|
|
200
|
+
},
|
|
201
|
+
__localTokenOwners: {
|
|
202
|
+
"--astryx-theme-neutral-color-status-fill-accent": "neutral",
|
|
203
|
+
"--astryx-theme-neutral-color-status-fill-success": "neutral",
|
|
204
|
+
"--astryx-theme-neutral-color-status-fill-warning": "neutral",
|
|
205
|
+
"--astryx-theme-neutral-color-status-fill-error": "neutral",
|
|
206
|
+
"--astryx-theme-neutral-color-on-tint-neutral": "neutral",
|
|
207
|
+
"--astryx-theme-neutral-color-on-tint-overlay-hover": "neutral",
|
|
208
|
+
"--astryx-theme-neutral-color-on-tint-overlay-pressed": "neutral"
|
|
209
|
+
},
|
|
210
|
+
__localTokenLineage: ["neutral"],
|
|
192
211
|
components: {
|
|
193
212
|
"heading": {
|
|
194
213
|
"level:1": {
|
|
@@ -293,24 +312,24 @@ export const neutralTheme = {
|
|
|
293
312
|
},
|
|
294
313
|
"badge": {
|
|
295
314
|
"variant:info": {
|
|
296
|
-
"backgroundColor": "
|
|
297
|
-
"color": "
|
|
315
|
+
"backgroundColor": "var(--astryx-theme-neutral-color-status-fill-accent)",
|
|
316
|
+
"color": "var(--color-on-accent)"
|
|
298
317
|
},
|
|
299
318
|
"variant:neutral": {
|
|
300
319
|
"backgroundColor": "var(--color-background-gray)",
|
|
301
320
|
"color": "var(--color-text-gray)"
|
|
302
321
|
},
|
|
303
322
|
"variant:success": {
|
|
304
|
-
"backgroundColor": "
|
|
305
|
-
"color": "
|
|
323
|
+
"backgroundColor": "var(--astryx-theme-neutral-color-status-fill-success)",
|
|
324
|
+
"color": "var(--color-on-success)"
|
|
306
325
|
},
|
|
307
326
|
"variant:warning": {
|
|
308
|
-
"backgroundColor": "
|
|
309
|
-
"color": "
|
|
327
|
+
"backgroundColor": "var(--astryx-theme-neutral-color-status-fill-warning)",
|
|
328
|
+
"color": "var(--color-on-warning)"
|
|
310
329
|
},
|
|
311
330
|
"variant:error": {
|
|
312
|
-
"backgroundColor": "
|
|
313
|
-
"color": "
|
|
331
|
+
"backgroundColor": "var(--astryx-theme-neutral-color-status-fill-error)",
|
|
332
|
+
"color": "var(--color-on-error)"
|
|
314
333
|
},
|
|
315
334
|
"variant:red": {
|
|
316
335
|
"backgroundColor": "var(--color-background-red)",
|
|
@@ -355,19 +374,32 @@ export const neutralTheme = {
|
|
|
355
374
|
},
|
|
356
375
|
"statusdot": {
|
|
357
376
|
"variant:success": {
|
|
358
|
-
"backgroundColor": "
|
|
377
|
+
"backgroundColor": "var(--astryx-theme-neutral-color-status-fill-success)"
|
|
359
378
|
},
|
|
360
379
|
"variant:warning": {
|
|
361
|
-
"backgroundColor": "
|
|
380
|
+
"backgroundColor": "var(--astryx-theme-neutral-color-status-fill-warning)"
|
|
362
381
|
},
|
|
363
382
|
"variant:error": {
|
|
364
|
-
"backgroundColor": "
|
|
383
|
+
"backgroundColor": "var(--astryx-theme-neutral-color-status-fill-error)"
|
|
365
384
|
},
|
|
366
385
|
"variant:accent": {
|
|
367
|
-
"backgroundColor": "
|
|
386
|
+
"backgroundColor": "var(--astryx-theme-neutral-color-status-fill-accent)"
|
|
387
|
+
}
|
|
388
|
+
},
|
|
389
|
+
"avatar-status-dot": {
|
|
390
|
+
"variant:success": {
|
|
391
|
+
"backgroundColor": "var(--astryx-theme-neutral-color-status-fill-success)"
|
|
392
|
+
},
|
|
393
|
+
"variant:error": {
|
|
394
|
+
"backgroundColor": "var(--astryx-theme-neutral-color-status-fill-error)"
|
|
368
395
|
}
|
|
369
396
|
},
|
|
370
397
|
"banner": {
|
|
398
|
+
"base": {
|
|
399
|
+
"--color-neutral": "var(--astryx-theme-neutral-color-on-tint-neutral)",
|
|
400
|
+
"--color-overlay-hover": "var(--astryx-theme-neutral-color-on-tint-overlay-hover)",
|
|
401
|
+
"--color-overlay-pressed": "var(--astryx-theme-neutral-color-on-tint-overlay-pressed)"
|
|
402
|
+
},
|
|
371
403
|
"status:info": {
|
|
372
404
|
"--color-accent-muted": "var(--color-background-blue)",
|
|
373
405
|
"--color-text-primary": "var(--color-text-blue)",
|
|
@@ -385,11 +417,26 @@ export const neutralTheme = {
|
|
|
385
417
|
"--color-warning": "var(--color-text-yellow)"
|
|
386
418
|
},
|
|
387
419
|
"status:error": {
|
|
420
|
+
"--color-error-muted": "var(--color-background-red)",
|
|
388
421
|
"--color-text-primary": "var(--color-text-red)",
|
|
389
422
|
"--color-text-secondary": "var(--color-text-red)",
|
|
390
423
|
"--color-error": "var(--color-text-red)"
|
|
391
424
|
}
|
|
392
425
|
},
|
|
426
|
+
"step-indicator": {
|
|
427
|
+
"status:accent": {
|
|
428
|
+
"--color-accent": "var(--astryx-theme-neutral-color-status-fill-accent)"
|
|
429
|
+
},
|
|
430
|
+
"status:success": {
|
|
431
|
+
"--color-success": "var(--astryx-theme-neutral-color-status-fill-success)"
|
|
432
|
+
},
|
|
433
|
+
"status:warning": {
|
|
434
|
+
"--color-warning": "var(--astryx-theme-neutral-color-status-fill-warning)"
|
|
435
|
+
},
|
|
436
|
+
"status:error": {
|
|
437
|
+
"--color-error": "var(--astryx-theme-neutral-color-status-fill-error)"
|
|
438
|
+
}
|
|
439
|
+
},
|
|
393
440
|
"switch": {
|
|
394
441
|
"base": {
|
|
395
442
|
"--color-background-gray": "var(--color-border-emphasized)"
|
|
@@ -400,16 +447,16 @@ export const neutralTheme = {
|
|
|
400
447
|
"--color-background-muted": "var(--color-border-emphasized)"
|
|
401
448
|
},
|
|
402
449
|
"variant:accent": {
|
|
403
|
-
"--color-accent": "
|
|
450
|
+
"--color-accent": "var(--astryx-theme-neutral-color-status-fill-accent)"
|
|
404
451
|
},
|
|
405
452
|
"variant:success": {
|
|
406
|
-
"--color-success": "
|
|
453
|
+
"--color-success": "var(--astryx-theme-neutral-color-status-fill-success)"
|
|
407
454
|
},
|
|
408
455
|
"variant:warning": {
|
|
409
|
-
"--color-warning": "
|
|
456
|
+
"--color-warning": "var(--astryx-theme-neutral-color-status-fill-warning)"
|
|
410
457
|
},
|
|
411
458
|
"variant:error": {
|
|
412
|
-
"--color-error": "
|
|
459
|
+
"--color-error": "var(--astryx-theme-neutral-color-status-fill-error)"
|
|
413
460
|
}
|
|
414
461
|
},
|
|
415
462
|
"card": {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"neutralTheme.d.ts","sourceRoot":"","sources":["../src/neutralTheme.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"neutralTheme.d.ts","sourceRoot":"","sources":["../src/neutralTheme.ts"],"names":[],"mappings":"AAwFA,eAAO,MAAM,YAAY,iDAgevB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"neutralTheme.test.d.ts","sourceRoot":"","sources":["../src/neutralTheme.test.ts"],"names":[],"mappings":""}
|
package/dist/source.js
CHANGED
|
@@ -68,7 +68,7 @@ var neutralIconRegistry = {
|
|
|
68
68
|
|
|
69
69
|
// src/neutralTheme.ts
|
|
70
70
|
var neutralSyntax = (0, import_theme.defineSyntaxTheme)({
|
|
71
|
-
name: "
|
|
71
|
+
name: "astryx-neutral",
|
|
72
72
|
tokens: {
|
|
73
73
|
keyword: ["#700084", "#efa8ff"],
|
|
74
74
|
// purple T30/T80
|
|
@@ -101,8 +101,30 @@ var neutralSyntax = (0, import_theme.defineSyntaxTheme)({
|
|
|
101
101
|
background: ["#fafafa", "#0a0a0a"]
|
|
102
102
|
}
|
|
103
103
|
});
|
|
104
|
+
var neutralLocalTokens = {
|
|
105
|
+
"--astryx-theme-neutral-color-status-fill-accent": ["#0074e2", "#6d9cfe"],
|
|
106
|
+
"--astryx-theme-neutral-color-status-fill-success": ["#198100", "#64af4c"],
|
|
107
|
+
"--astryx-theme-neutral-color-status-fill-warning": "#ffce2f",
|
|
108
|
+
"--astryx-theme-neutral-color-status-fill-error": ["#c9303a", "#ff705d"],
|
|
109
|
+
"--astryx-theme-neutral-color-on-tint-neutral": ["#0a0a0a4D", "#fafafa4D"],
|
|
110
|
+
"--astryx-theme-neutral-color-on-tint-overlay-hover": [
|
|
111
|
+
"#0a0a0a1A",
|
|
112
|
+
"#fafafa1A"
|
|
113
|
+
],
|
|
114
|
+
"--astryx-theme-neutral-color-on-tint-overlay-pressed": [
|
|
115
|
+
"#0a0a0a33",
|
|
116
|
+
"#fafafa33"
|
|
117
|
+
]
|
|
118
|
+
};
|
|
119
|
+
var statusFill = {
|
|
120
|
+
accent: "var(--astryx-theme-neutral-color-status-fill-accent)",
|
|
121
|
+
success: "var(--astryx-theme-neutral-color-status-fill-success)",
|
|
122
|
+
warning: "var(--astryx-theme-neutral-color-status-fill-warning)",
|
|
123
|
+
error: "var(--astryx-theme-neutral-color-status-fill-error)"
|
|
124
|
+
};
|
|
104
125
|
var neutralTheme = (0, import_theme.defineTheme)({
|
|
105
126
|
name: "neutral",
|
|
127
|
+
localTokens: neutralLocalTokens,
|
|
106
128
|
// Typography: Figtree across body, heading, and display sizes (display
|
|
107
129
|
// size tokens inherit from heading.family). Monospace stays as the
|
|
108
130
|
// platform default for code.
|
|
@@ -378,84 +400,33 @@ var neutralTheme = (0, import_theme.defineTheme)({
|
|
|
378
400
|
"--shadow-inset-error": "inset 0px 0px 0px 2px #e33f4a4D"
|
|
379
401
|
},
|
|
380
402
|
components: {
|
|
381
|
-
// =========================================================================
|
|
382
|
-
// Button — primary gets white text, secondary gets a border, destructive
|
|
383
|
-
// uses the OKLCH red filled treatment.
|
|
384
|
-
// =========================================================================
|
|
385
403
|
button: {
|
|
386
404
|
"variant:destructive": {
|
|
387
405
|
backgroundColor: "var(--color-error-muted)",
|
|
388
|
-
// locked pastel red bg
|
|
389
406
|
color: "var(--color-error)"
|
|
390
|
-
// locked T30 red — matches banner/input error text
|
|
391
407
|
}
|
|
392
408
|
},
|
|
393
|
-
// =========================================================================
|
|
394
|
-
// Badge —
|
|
395
|
-
// Semantic (info/success/warning/error): filled saturated T50 + contrasting
|
|
396
|
-
// text (white, or dark on yellow). The filled-button rule from #2150
|
|
397
|
-
// §3 — text contrast locks the bg tone, so this stays at T50 in
|
|
398
|
-
// BOTH modes, unlike pastel surfaces which invert by mode.
|
|
399
|
-
// Categorical (blue/green/red/orange/etc.): pastel-tinted hue surface +
|
|
400
|
-
// colored text — light mode = soft T87-T90 + dark T30 text; dark mode
|
|
401
|
-
// = T20 tinted + T80 light pastel text (sources: --color-background-X
|
|
402
|
-
// and --color-text-X tokens).
|
|
403
|
-
// Neutral: light gray bg + dark text (or inverted in dark mode).
|
|
404
|
-
// =========================================================================
|
|
405
409
|
badge: {
|
|
406
|
-
// Semantic — filled saturated bg + contrasting text.
|
|
407
|
-
// Light: vivid T45-T55 from the OKLCH palette + white text
|
|
408
|
-
// (~4.5-5:1 — Material/Linear/Vercel pop).
|
|
409
|
-
// Dark : T60 stop from the dark-mode tonal palette (chroma×0.85,
|
|
410
|
-
// +5 tone-lift taper from issue #2150 §4) + DARK text.
|
|
411
|
-
// T60+white fails AA-large (~2.7:1); T60+dark hits 6.6-7:1
|
|
412
|
-
// and tames the §4 vibration. Same dark-text-on-bright-bg
|
|
413
|
-
// treatment that warning yellow uses in both modes.
|
|
414
410
|
"variant:info": {
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
backgroundColor: "light-dark(#0074e2, #6d9cfe)",
|
|
418
|
-
color: "light-dark(#ffffff, #171717)"
|
|
411
|
+
backgroundColor: statusFill.accent,
|
|
412
|
+
color: "var(--color-on-accent)"
|
|
419
413
|
},
|
|
420
414
|
"variant:neutral": {
|
|
421
|
-
// Mirrors the gray categorical badge — same neutral chip treatment
|
|
422
|
-
// (Neutral 200 light / semi-transparent white wash dark) sourced
|
|
423
|
-
// from the gray hue tokens, so a single change at the token layer
|
|
424
|
-
// updates both variants.
|
|
425
415
|
backgroundColor: "var(--color-background-gray)",
|
|
426
416
|
color: "var(--color-text-gray)"
|
|
427
417
|
},
|
|
428
418
|
"variant:success": {
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
backgroundColor: "light-dark(#198100, #64af4c)",
|
|
432
|
-
color: "light-dark(#ffffff, #171717)"
|
|
419
|
+
backgroundColor: statusFill.success,
|
|
420
|
+
color: "var(--color-on-success)"
|
|
433
421
|
},
|
|
434
422
|
"variant:warning": {
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
// suffer from the §4 vibration concern.
|
|
438
|
-
backgroundColor: "#ffce2f",
|
|
439
|
-
color: "#171717"
|
|
423
|
+
backgroundColor: statusFill.warning,
|
|
424
|
+
color: "var(--color-on-warning)"
|
|
440
425
|
},
|
|
441
426
|
"variant:error": {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
// large-text allowance. One tonal step down holds the hue
|
|
445
|
-
// (OKLCH H 21.9 -> 22.8, C 0.200 -> 0.189) and reaches 5.29:1.
|
|
446
|
-
// Dark : T60 stop from dark-mode tonal palette of Tailwind red-600
|
|
447
|
-
// source #dc2626 (kept on H=27 alarm-red rather than coral).
|
|
448
|
-
// Dark text on it is 6.60:1 and unchanged.
|
|
449
|
-
backgroundColor: "light-dark(#c9303a, #ff705d)",
|
|
450
|
-
color: "light-dark(#ffffff, #171717)"
|
|
427
|
+
backgroundColor: statusFill.error,
|
|
428
|
+
color: "var(--color-on-error)"
|
|
451
429
|
},
|
|
452
|
-
// Categorical — bg + text reference the per-hue tokens, so behavior
|
|
453
|
-
// tracks the categorical palette automatically:
|
|
454
|
-
// Light: pastel T87-T90 bg + dark T30 colored text (low-key chip)
|
|
455
|
-
// Dark : tinted T20 bg + light T80 colored text (per #2150 §5,
|
|
456
|
-
// inverted from light to avoid the "pastel-in-both-modes"
|
|
457
|
-
// anti-pattern that makes locked light pastels glow on a
|
|
458
|
-
// dark body)
|
|
459
430
|
"variant:red": {
|
|
460
431
|
backgroundColor: "var(--color-background-red)",
|
|
461
432
|
color: "var(--color-text-red)"
|
|
@@ -497,63 +468,28 @@ var neutralTheme = (0, import_theme.defineTheme)({
|
|
|
497
468
|
color: "var(--color-text-gray)"
|
|
498
469
|
}
|
|
499
470
|
},
|
|
500
|
-
// =========================================================================
|
|
501
|
-
// StatusDot — fill uses the SAME vivid stops as the filled semantic Badge
|
|
502
|
-
// (and ProgressBar), so a dot and its badge read as one status language.
|
|
503
|
-
//
|
|
504
|
-
// The default component maps each variant to a raw semantic token
|
|
505
|
-
// (--color-success / --color-error / --color-warning / --color-icon-
|
|
506
|
-
// secondary), which in light mode are the dark T30/T40 stops meant to
|
|
507
|
-
// sit as TEXT on a pastel surface — as a solid dot they read muddy
|
|
508
|
-
// (dark green / maroon / brown). Redirect them to the badge fills.
|
|
509
|
-
//
|
|
510
|
-
// success → badge success bg (green T45 / dark-ramp T60)
|
|
511
|
-
// warning → badge warning bg (yellow T85, same hex both modes)
|
|
512
|
-
// error → badge error bg (red T58 / dark-ramp T60)
|
|
513
|
-
// accent → badge info bg (blue T50 / dark-ramp T60) — the
|
|
514
|
-
// StatusDot "accent" is the info/attention color, so it
|
|
515
|
-
// pairs with the info badge rather than --color-accent
|
|
516
|
-
// (near-black #262626, the darkest offender).
|
|
517
|
-
//
|
|
518
|
-
// `neutral` is intentionally NOT overridden: the neutral badge bg is a
|
|
519
|
-
// near-invisible light gray (--color-background-gray #e5e5e5 / 10% white
|
|
520
|
-
// wash), fine as a large pill but unreadable as an 8px dot. It keeps the
|
|
521
|
-
// component default's visible mid-gray (--color-icon-secondary), which is
|
|
522
|
-
// not among the "too dark" cases.
|
|
523
|
-
// =========================================================================
|
|
524
471
|
statusdot: {
|
|
525
|
-
"variant:success": { backgroundColor:
|
|
526
|
-
"variant:warning": { backgroundColor:
|
|
527
|
-
"variant:error": { backgroundColor:
|
|
528
|
-
"variant:accent": { backgroundColor:
|
|
472
|
+
"variant:success": { backgroundColor: statusFill.success },
|
|
473
|
+
"variant:warning": { backgroundColor: statusFill.warning },
|
|
474
|
+
"variant:error": { backgroundColor: statusFill.error },
|
|
475
|
+
"variant:accent": { backgroundColor: statusFill.accent }
|
|
476
|
+
},
|
|
477
|
+
"avatar-status-dot": {
|
|
478
|
+
"variant:success": { backgroundColor: statusFill.success },
|
|
479
|
+
"variant:error": { backgroundColor: statusFill.error }
|
|
529
480
|
},
|
|
530
|
-
// =========================================================================
|
|
531
|
-
// Banner — sits on a hue-tinted surface with colored text/icon:
|
|
532
|
-
// Light: pastel T90 bg (pulled from --color-{X}-muted / --color-background-blue)
|
|
533
|
-
// + dark T30 colored text (--color-text-{hue}).
|
|
534
|
-
// Dark : tinted T20 bg (same tokens, dark slot) + light T80 colored text.
|
|
535
|
-
// Per #2150 §5 — large hue-tinted surfaces in dark mode invert
|
|
536
|
-
// to a deep tinted bg + light text rather than locking the
|
|
537
|
-
// light-mode pastel.
|
|
538
|
-
//
|
|
539
|
-
// The inner-header *-muted token carries the tinted background for every
|
|
540
|
-
// status, info included. A theme override that sets a plain CSS property
|
|
541
|
-
// instead lands in @layer astryx-theme, which StyleX's @layer priority4
|
|
542
|
-
// outranks, so `backgroundColor` here would silently do nothing and the
|
|
543
|
-
// info banner would paint no background at all.
|
|
544
|
-
//
|
|
545
|
-
// Status overrides reference --color-text-{hue} so text/icon colors
|
|
546
|
-
// stay in sync with the palette anchors automatically.
|
|
547
481
|
banner: {
|
|
482
|
+
base: {
|
|
483
|
+
"--color-neutral": "var(--astryx-theme-neutral-color-on-tint-neutral)",
|
|
484
|
+
"--color-overlay-hover": "var(--astryx-theme-neutral-color-on-tint-overlay-hover)",
|
|
485
|
+
"--color-overlay-pressed": "var(--astryx-theme-neutral-color-on-tint-overlay-pressed)"
|
|
486
|
+
},
|
|
548
487
|
"status:info": {
|
|
549
488
|
"--color-accent-muted": "var(--color-background-blue)",
|
|
550
489
|
"--color-text-primary": "var(--color-text-blue)",
|
|
551
490
|
"--color-text-secondary": "var(--color-text-blue)",
|
|
552
491
|
"--color-accent": "var(--color-text-blue)"
|
|
553
492
|
},
|
|
554
|
-
// success/warning/error banner bgs come from --color-{X}-muted, which
|
|
555
|
-
// already carries the correct light/dark tinted values. We only need
|
|
556
|
-
// to redirect the text/icon to the palette colored stop.
|
|
557
493
|
"status:success": {
|
|
558
494
|
"--color-text-primary": "var(--color-text-green)",
|
|
559
495
|
"--color-text-secondary": "var(--color-text-green)",
|
|
@@ -565,27 +501,18 @@ var neutralTheme = (0, import_theme.defineTheme)({
|
|
|
565
501
|
"--color-warning": "var(--color-text-yellow)"
|
|
566
502
|
},
|
|
567
503
|
"status:error": {
|
|
504
|
+
"--color-error-muted": "var(--color-background-red)",
|
|
568
505
|
"--color-text-primary": "var(--color-text-red)",
|
|
569
506
|
"--color-text-secondary": "var(--color-text-red)",
|
|
570
507
|
"--color-error": "var(--color-text-red)"
|
|
571
508
|
}
|
|
572
509
|
},
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
// dark T20). Verified all six combinations clear AA non-text 3:1.
|
|
580
|
-
// =========================================================================
|
|
581
|
-
// =========================================================================
|
|
582
|
-
// Switch — off-state track uses the same lifted-neutral surface as the
|
|
583
|
-
// ProgressBar track (--color-border-emphasized). Aligns the two
|
|
584
|
-
// "channel-on-body" components so their off-states share one visual
|
|
585
|
-
// language: light T85 #d4d4d4 sits one step darker than the body T95
|
|
586
|
-
// bg, dark T35 #525252 sits one step lighter than the body T10. Each
|
|
587
|
-
// is a defined channel, not a wash that blends in.
|
|
588
|
-
// =========================================================================
|
|
510
|
+
"step-indicator": {
|
|
511
|
+
"status:accent": { "--color-accent": statusFill.accent },
|
|
512
|
+
"status:success": { "--color-success": statusFill.success },
|
|
513
|
+
"status:warning": { "--color-warning": statusFill.warning },
|
|
514
|
+
"status:error": { "--color-error": statusFill.error }
|
|
515
|
+
},
|
|
589
516
|
switch: {
|
|
590
517
|
base: {
|
|
591
518
|
"--color-background-gray": "var(--color-border-emphasized)"
|
|
@@ -593,44 +520,26 @@ var neutralTheme = (0, import_theme.defineTheme)({
|
|
|
593
520
|
},
|
|
594
521
|
progressbar: {
|
|
595
522
|
base: {
|
|
596
|
-
// Track uses --color-background-muted; override it to
|
|
597
|
-
// --color-border-emphasized (Neutral T85 #d4d4d4 in light mode) so
|
|
598
|
-
// the track is clearly darker than the body bg (Neutral T95 #f1f1f1)
|
|
599
|
-
// and reads as a defined channel rather than blending in. Dark
|
|
600
|
-
// mode inherits T35 #525252 — same one-step-lighter behavior.
|
|
601
523
|
"--color-background-muted": "var(--color-border-emphasized)"
|
|
602
524
|
},
|
|
603
|
-
// Vivid stops match the filled semantic badge colors (info/success/
|
|
604
|
-
// warning/error variants in the badge override above). Same hex
|
|
605
|
-
// values; documented per role with palette provenance.
|
|
606
525
|
"variant:accent": {
|
|
607
|
-
|
|
608
|
-
"--color-accent": "#0074e2"
|
|
526
|
+
"--color-accent": statusFill.accent
|
|
609
527
|
},
|
|
610
528
|
"variant:success": {
|
|
611
|
-
|
|
612
|
-
"--color-success": "#198100"
|
|
529
|
+
"--color-success": statusFill.success
|
|
613
530
|
},
|
|
614
531
|
"variant:warning": {
|
|
615
|
-
|
|
616
|
-
"--color-warning": "#ffce2f"
|
|
532
|
+
"--color-warning": statusFill.warning
|
|
617
533
|
},
|
|
618
534
|
"variant:error": {
|
|
619
|
-
|
|
620
|
-
"--color-error": "#c9303a"
|
|
535
|
+
"--color-error": statusFill.error
|
|
621
536
|
}
|
|
622
537
|
},
|
|
623
|
-
// =========================================================================
|
|
624
|
-
// Card — tighter padding via public card padding token
|
|
625
|
-
// =========================================================================
|
|
626
538
|
card: {
|
|
627
539
|
base: {
|
|
628
540
|
padding: "var(--spacing-3)"
|
|
629
541
|
}
|
|
630
542
|
},
|
|
631
|
-
// =========================================================================
|
|
632
|
-
// Section — tighter padding via public section padding token
|
|
633
|
-
// =========================================================================
|
|
634
543
|
section: {
|
|
635
544
|
base: {
|
|
636
545
|
padding: "var(--spacing-3)"
|
package/dist/source.mjs
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
// src/neutralTheme.ts
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
defineTheme,
|
|
4
|
+
defineSyntaxTheme
|
|
5
|
+
} from "@astryxdesign/core/theme";
|
|
3
6
|
|
|
4
7
|
// src/icons.tsx
|
|
5
8
|
import {
|
|
@@ -70,7 +73,7 @@ var neutralIconRegistry = {
|
|
|
70
73
|
|
|
71
74
|
// src/neutralTheme.ts
|
|
72
75
|
var neutralSyntax = defineSyntaxTheme({
|
|
73
|
-
name: "
|
|
76
|
+
name: "astryx-neutral",
|
|
74
77
|
tokens: {
|
|
75
78
|
keyword: ["#700084", "#efa8ff"],
|
|
76
79
|
// purple T30/T80
|
|
@@ -103,8 +106,30 @@ var neutralSyntax = defineSyntaxTheme({
|
|
|
103
106
|
background: ["#fafafa", "#0a0a0a"]
|
|
104
107
|
}
|
|
105
108
|
});
|
|
109
|
+
var neutralLocalTokens = {
|
|
110
|
+
"--astryx-theme-neutral-color-status-fill-accent": ["#0074e2", "#6d9cfe"],
|
|
111
|
+
"--astryx-theme-neutral-color-status-fill-success": ["#198100", "#64af4c"],
|
|
112
|
+
"--astryx-theme-neutral-color-status-fill-warning": "#ffce2f",
|
|
113
|
+
"--astryx-theme-neutral-color-status-fill-error": ["#c9303a", "#ff705d"],
|
|
114
|
+
"--astryx-theme-neutral-color-on-tint-neutral": ["#0a0a0a4D", "#fafafa4D"],
|
|
115
|
+
"--astryx-theme-neutral-color-on-tint-overlay-hover": [
|
|
116
|
+
"#0a0a0a1A",
|
|
117
|
+
"#fafafa1A"
|
|
118
|
+
],
|
|
119
|
+
"--astryx-theme-neutral-color-on-tint-overlay-pressed": [
|
|
120
|
+
"#0a0a0a33",
|
|
121
|
+
"#fafafa33"
|
|
122
|
+
]
|
|
123
|
+
};
|
|
124
|
+
var statusFill = {
|
|
125
|
+
accent: "var(--astryx-theme-neutral-color-status-fill-accent)",
|
|
126
|
+
success: "var(--astryx-theme-neutral-color-status-fill-success)",
|
|
127
|
+
warning: "var(--astryx-theme-neutral-color-status-fill-warning)",
|
|
128
|
+
error: "var(--astryx-theme-neutral-color-status-fill-error)"
|
|
129
|
+
};
|
|
106
130
|
var neutralTheme = defineTheme({
|
|
107
131
|
name: "neutral",
|
|
132
|
+
localTokens: neutralLocalTokens,
|
|
108
133
|
// Typography: Figtree across body, heading, and display sizes (display
|
|
109
134
|
// size tokens inherit from heading.family). Monospace stays as the
|
|
110
135
|
// platform default for code.
|
|
@@ -380,84 +405,33 @@ var neutralTheme = defineTheme({
|
|
|
380
405
|
"--shadow-inset-error": "inset 0px 0px 0px 2px #e33f4a4D"
|
|
381
406
|
},
|
|
382
407
|
components: {
|
|
383
|
-
// =========================================================================
|
|
384
|
-
// Button — primary gets white text, secondary gets a border, destructive
|
|
385
|
-
// uses the OKLCH red filled treatment.
|
|
386
|
-
// =========================================================================
|
|
387
408
|
button: {
|
|
388
409
|
"variant:destructive": {
|
|
389
410
|
backgroundColor: "var(--color-error-muted)",
|
|
390
|
-
// locked pastel red bg
|
|
391
411
|
color: "var(--color-error)"
|
|
392
|
-
// locked T30 red — matches banner/input error text
|
|
393
412
|
}
|
|
394
413
|
},
|
|
395
|
-
// =========================================================================
|
|
396
|
-
// Badge —
|
|
397
|
-
// Semantic (info/success/warning/error): filled saturated T50 + contrasting
|
|
398
|
-
// text (white, or dark on yellow). The filled-button rule from #2150
|
|
399
|
-
// §3 — text contrast locks the bg tone, so this stays at T50 in
|
|
400
|
-
// BOTH modes, unlike pastel surfaces which invert by mode.
|
|
401
|
-
// Categorical (blue/green/red/orange/etc.): pastel-tinted hue surface +
|
|
402
|
-
// colored text — light mode = soft T87-T90 + dark T30 text; dark mode
|
|
403
|
-
// = T20 tinted + T80 light pastel text (sources: --color-background-X
|
|
404
|
-
// and --color-text-X tokens).
|
|
405
|
-
// Neutral: light gray bg + dark text (or inverted in dark mode).
|
|
406
|
-
// =========================================================================
|
|
407
414
|
badge: {
|
|
408
|
-
// Semantic — filled saturated bg + contrasting text.
|
|
409
|
-
// Light: vivid T45-T55 from the OKLCH palette + white text
|
|
410
|
-
// (~4.5-5:1 — Material/Linear/Vercel pop).
|
|
411
|
-
// Dark : T60 stop from the dark-mode tonal palette (chroma×0.85,
|
|
412
|
-
// +5 tone-lift taper from issue #2150 §4) + DARK text.
|
|
413
|
-
// T60+white fails AA-large (~2.7:1); T60+dark hits 6.6-7:1
|
|
414
|
-
// and tames the §4 vibration. Same dark-text-on-bright-bg
|
|
415
|
-
// treatment that warning yellow uses in both modes.
|
|
416
415
|
"variant:info": {
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
backgroundColor: "light-dark(#0074e2, #6d9cfe)",
|
|
420
|
-
color: "light-dark(#ffffff, #171717)"
|
|
416
|
+
backgroundColor: statusFill.accent,
|
|
417
|
+
color: "var(--color-on-accent)"
|
|
421
418
|
},
|
|
422
419
|
"variant:neutral": {
|
|
423
|
-
// Mirrors the gray categorical badge — same neutral chip treatment
|
|
424
|
-
// (Neutral 200 light / semi-transparent white wash dark) sourced
|
|
425
|
-
// from the gray hue tokens, so a single change at the token layer
|
|
426
|
-
// updates both variants.
|
|
427
420
|
backgroundColor: "var(--color-background-gray)",
|
|
428
421
|
color: "var(--color-text-gray)"
|
|
429
422
|
},
|
|
430
423
|
"variant:success": {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
backgroundColor: "light-dark(#198100, #64af4c)",
|
|
434
|
-
color: "light-dark(#ffffff, #171717)"
|
|
424
|
+
backgroundColor: statusFill.success,
|
|
425
|
+
color: "var(--color-on-success)"
|
|
435
426
|
},
|
|
436
427
|
"variant:warning": {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
// suffer from the §4 vibration concern.
|
|
440
|
-
backgroundColor: "#ffce2f",
|
|
441
|
-
color: "#171717"
|
|
428
|
+
backgroundColor: statusFill.warning,
|
|
429
|
+
color: "var(--color-on-warning)"
|
|
442
430
|
},
|
|
443
431
|
"variant:error": {
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
// large-text allowance. One tonal step down holds the hue
|
|
447
|
-
// (OKLCH H 21.9 -> 22.8, C 0.200 -> 0.189) and reaches 5.29:1.
|
|
448
|
-
// Dark : T60 stop from dark-mode tonal palette of Tailwind red-600
|
|
449
|
-
// source #dc2626 (kept on H=27 alarm-red rather than coral).
|
|
450
|
-
// Dark text on it is 6.60:1 and unchanged.
|
|
451
|
-
backgroundColor: "light-dark(#c9303a, #ff705d)",
|
|
452
|
-
color: "light-dark(#ffffff, #171717)"
|
|
432
|
+
backgroundColor: statusFill.error,
|
|
433
|
+
color: "var(--color-on-error)"
|
|
453
434
|
},
|
|
454
|
-
// Categorical — bg + text reference the per-hue tokens, so behavior
|
|
455
|
-
// tracks the categorical palette automatically:
|
|
456
|
-
// Light: pastel T87-T90 bg + dark T30 colored text (low-key chip)
|
|
457
|
-
// Dark : tinted T20 bg + light T80 colored text (per #2150 §5,
|
|
458
|
-
// inverted from light to avoid the "pastel-in-both-modes"
|
|
459
|
-
// anti-pattern that makes locked light pastels glow on a
|
|
460
|
-
// dark body)
|
|
461
435
|
"variant:red": {
|
|
462
436
|
backgroundColor: "var(--color-background-red)",
|
|
463
437
|
color: "var(--color-text-red)"
|
|
@@ -499,63 +473,28 @@ var neutralTheme = defineTheme({
|
|
|
499
473
|
color: "var(--color-text-gray)"
|
|
500
474
|
}
|
|
501
475
|
},
|
|
502
|
-
// =========================================================================
|
|
503
|
-
// StatusDot — fill uses the SAME vivid stops as the filled semantic Badge
|
|
504
|
-
// (and ProgressBar), so a dot and its badge read as one status language.
|
|
505
|
-
//
|
|
506
|
-
// The default component maps each variant to a raw semantic token
|
|
507
|
-
// (--color-success / --color-error / --color-warning / --color-icon-
|
|
508
|
-
// secondary), which in light mode are the dark T30/T40 stops meant to
|
|
509
|
-
// sit as TEXT on a pastel surface — as a solid dot they read muddy
|
|
510
|
-
// (dark green / maroon / brown). Redirect them to the badge fills.
|
|
511
|
-
//
|
|
512
|
-
// success → badge success bg (green T45 / dark-ramp T60)
|
|
513
|
-
// warning → badge warning bg (yellow T85, same hex both modes)
|
|
514
|
-
// error → badge error bg (red T58 / dark-ramp T60)
|
|
515
|
-
// accent → badge info bg (blue T50 / dark-ramp T60) — the
|
|
516
|
-
// StatusDot "accent" is the info/attention color, so it
|
|
517
|
-
// pairs with the info badge rather than --color-accent
|
|
518
|
-
// (near-black #262626, the darkest offender).
|
|
519
|
-
//
|
|
520
|
-
// `neutral` is intentionally NOT overridden: the neutral badge bg is a
|
|
521
|
-
// near-invisible light gray (--color-background-gray #e5e5e5 / 10% white
|
|
522
|
-
// wash), fine as a large pill but unreadable as an 8px dot. It keeps the
|
|
523
|
-
// component default's visible mid-gray (--color-icon-secondary), which is
|
|
524
|
-
// not among the "too dark" cases.
|
|
525
|
-
// =========================================================================
|
|
526
476
|
statusdot: {
|
|
527
|
-
"variant:success": { backgroundColor:
|
|
528
|
-
"variant:warning": { backgroundColor:
|
|
529
|
-
"variant:error": { backgroundColor:
|
|
530
|
-
"variant:accent": { backgroundColor:
|
|
477
|
+
"variant:success": { backgroundColor: statusFill.success },
|
|
478
|
+
"variant:warning": { backgroundColor: statusFill.warning },
|
|
479
|
+
"variant:error": { backgroundColor: statusFill.error },
|
|
480
|
+
"variant:accent": { backgroundColor: statusFill.accent }
|
|
481
|
+
},
|
|
482
|
+
"avatar-status-dot": {
|
|
483
|
+
"variant:success": { backgroundColor: statusFill.success },
|
|
484
|
+
"variant:error": { backgroundColor: statusFill.error }
|
|
531
485
|
},
|
|
532
|
-
// =========================================================================
|
|
533
|
-
// Banner — sits on a hue-tinted surface with colored text/icon:
|
|
534
|
-
// Light: pastel T90 bg (pulled from --color-{X}-muted / --color-background-blue)
|
|
535
|
-
// + dark T30 colored text (--color-text-{hue}).
|
|
536
|
-
// Dark : tinted T20 bg (same tokens, dark slot) + light T80 colored text.
|
|
537
|
-
// Per #2150 §5 — large hue-tinted surfaces in dark mode invert
|
|
538
|
-
// to a deep tinted bg + light text rather than locking the
|
|
539
|
-
// light-mode pastel.
|
|
540
|
-
//
|
|
541
|
-
// The inner-header *-muted token carries the tinted background for every
|
|
542
|
-
// status, info included. A theme override that sets a plain CSS property
|
|
543
|
-
// instead lands in @layer astryx-theme, which StyleX's @layer priority4
|
|
544
|
-
// outranks, so `backgroundColor` here would silently do nothing and the
|
|
545
|
-
// info banner would paint no background at all.
|
|
546
|
-
//
|
|
547
|
-
// Status overrides reference --color-text-{hue} so text/icon colors
|
|
548
|
-
// stay in sync with the palette anchors automatically.
|
|
549
486
|
banner: {
|
|
487
|
+
base: {
|
|
488
|
+
"--color-neutral": "var(--astryx-theme-neutral-color-on-tint-neutral)",
|
|
489
|
+
"--color-overlay-hover": "var(--astryx-theme-neutral-color-on-tint-overlay-hover)",
|
|
490
|
+
"--color-overlay-pressed": "var(--astryx-theme-neutral-color-on-tint-overlay-pressed)"
|
|
491
|
+
},
|
|
550
492
|
"status:info": {
|
|
551
493
|
"--color-accent-muted": "var(--color-background-blue)",
|
|
552
494
|
"--color-text-primary": "var(--color-text-blue)",
|
|
553
495
|
"--color-text-secondary": "var(--color-text-blue)",
|
|
554
496
|
"--color-accent": "var(--color-text-blue)"
|
|
555
497
|
},
|
|
556
|
-
// success/warning/error banner bgs come from --color-{X}-muted, which
|
|
557
|
-
// already carries the correct light/dark tinted values. We only need
|
|
558
|
-
// to redirect the text/icon to the palette colored stop.
|
|
559
498
|
"status:success": {
|
|
560
499
|
"--color-text-primary": "var(--color-text-green)",
|
|
561
500
|
"--color-text-secondary": "var(--color-text-green)",
|
|
@@ -567,27 +506,18 @@ var neutralTheme = defineTheme({
|
|
|
567
506
|
"--color-warning": "var(--color-text-yellow)"
|
|
568
507
|
},
|
|
569
508
|
"status:error": {
|
|
509
|
+
"--color-error-muted": "var(--color-background-red)",
|
|
570
510
|
"--color-text-primary": "var(--color-text-red)",
|
|
571
511
|
"--color-text-secondary": "var(--color-text-red)",
|
|
572
512
|
"--color-error": "var(--color-text-red)"
|
|
573
513
|
}
|
|
574
514
|
},
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
// dark T20). Verified all six combinations clear AA non-text 3:1.
|
|
582
|
-
// =========================================================================
|
|
583
|
-
// =========================================================================
|
|
584
|
-
// Switch — off-state track uses the same lifted-neutral surface as the
|
|
585
|
-
// ProgressBar track (--color-border-emphasized). Aligns the two
|
|
586
|
-
// "channel-on-body" components so their off-states share one visual
|
|
587
|
-
// language: light T85 #d4d4d4 sits one step darker than the body T95
|
|
588
|
-
// bg, dark T35 #525252 sits one step lighter than the body T10. Each
|
|
589
|
-
// is a defined channel, not a wash that blends in.
|
|
590
|
-
// =========================================================================
|
|
515
|
+
"step-indicator": {
|
|
516
|
+
"status:accent": { "--color-accent": statusFill.accent },
|
|
517
|
+
"status:success": { "--color-success": statusFill.success },
|
|
518
|
+
"status:warning": { "--color-warning": statusFill.warning },
|
|
519
|
+
"status:error": { "--color-error": statusFill.error }
|
|
520
|
+
},
|
|
591
521
|
switch: {
|
|
592
522
|
base: {
|
|
593
523
|
"--color-background-gray": "var(--color-border-emphasized)"
|
|
@@ -595,44 +525,26 @@ var neutralTheme = defineTheme({
|
|
|
595
525
|
},
|
|
596
526
|
progressbar: {
|
|
597
527
|
base: {
|
|
598
|
-
// Track uses --color-background-muted; override it to
|
|
599
|
-
// --color-border-emphasized (Neutral T85 #d4d4d4 in light mode) so
|
|
600
|
-
// the track is clearly darker than the body bg (Neutral T95 #f1f1f1)
|
|
601
|
-
// and reads as a defined channel rather than blending in. Dark
|
|
602
|
-
// mode inherits T35 #525252 — same one-step-lighter behavior.
|
|
603
528
|
"--color-background-muted": "var(--color-border-emphasized)"
|
|
604
529
|
},
|
|
605
|
-
// Vivid stops match the filled semantic badge colors (info/success/
|
|
606
|
-
// warning/error variants in the badge override above). Same hex
|
|
607
|
-
// values; documented per role with palette provenance.
|
|
608
530
|
"variant:accent": {
|
|
609
|
-
|
|
610
|
-
"--color-accent": "#0074e2"
|
|
531
|
+
"--color-accent": statusFill.accent
|
|
611
532
|
},
|
|
612
533
|
"variant:success": {
|
|
613
|
-
|
|
614
|
-
"--color-success": "#198100"
|
|
534
|
+
"--color-success": statusFill.success
|
|
615
535
|
},
|
|
616
536
|
"variant:warning": {
|
|
617
|
-
|
|
618
|
-
"--color-warning": "#ffce2f"
|
|
537
|
+
"--color-warning": statusFill.warning
|
|
619
538
|
},
|
|
620
539
|
"variant:error": {
|
|
621
|
-
|
|
622
|
-
"--color-error": "#c9303a"
|
|
540
|
+
"--color-error": statusFill.error
|
|
623
541
|
}
|
|
624
542
|
},
|
|
625
|
-
// =========================================================================
|
|
626
|
-
// Card — tighter padding via public card padding token
|
|
627
|
-
// =========================================================================
|
|
628
543
|
card: {
|
|
629
544
|
base: {
|
|
630
545
|
padding: "var(--spacing-3)"
|
|
631
546
|
}
|
|
632
547
|
},
|
|
633
|
-
// =========================================================================
|
|
634
|
-
// Section — tighter padding via public section padding token
|
|
635
|
-
// =========================================================================
|
|
636
548
|
section: {
|
|
637
549
|
base: {
|
|
638
550
|
padding: "var(--spacing-3)"
|
package/dist/theme.css
CHANGED
|
@@ -315,6 +315,13 @@
|
|
|
315
315
|
--shadow-inset-success: inset 0px 0px 0px 2px #1981004D;
|
|
316
316
|
--shadow-inset-warning: inset 0px 0px 0px 2px #ffce2f4D;
|
|
317
317
|
--shadow-inset-error: inset 0px 0px 0px 2px #e33f4a4D;
|
|
318
|
+
--astryx-theme-neutral-color-status-fill-accent: light-dark(#0074e2, #6d9cfe);
|
|
319
|
+
--astryx-theme-neutral-color-status-fill-success: light-dark(#198100, #64af4c);
|
|
320
|
+
--astryx-theme-neutral-color-status-fill-warning: #ffce2f;
|
|
321
|
+
--astryx-theme-neutral-color-status-fill-error: light-dark(#c9303a, #ff705d);
|
|
322
|
+
--astryx-theme-neutral-color-on-tint-neutral: light-dark(#0a0a0a4D, #fafafa4D);
|
|
323
|
+
--astryx-theme-neutral-color-on-tint-overlay-hover: light-dark(#0a0a0a1A, #fafafa1A);
|
|
324
|
+
--astryx-theme-neutral-color-on-tint-overlay-pressed: light-dark(#0a0a0a33, #fafafa33);
|
|
318
325
|
}
|
|
319
326
|
|
|
320
327
|
.astryx-heading.level-1 {
|
|
@@ -431,8 +438,8 @@
|
|
|
431
438
|
}
|
|
432
439
|
|
|
433
440
|
.astryx-badge.info {
|
|
434
|
-
background-color:
|
|
435
|
-
color:
|
|
441
|
+
background-color: var(--astryx-theme-neutral-color-status-fill-accent);
|
|
442
|
+
color: var(--color-on-accent);
|
|
436
443
|
}
|
|
437
444
|
|
|
438
445
|
.astryx-badge.neutral {
|
|
@@ -441,18 +448,18 @@
|
|
|
441
448
|
}
|
|
442
449
|
|
|
443
450
|
.astryx-badge.success {
|
|
444
|
-
background-color:
|
|
445
|
-
color:
|
|
451
|
+
background-color: var(--astryx-theme-neutral-color-status-fill-success);
|
|
452
|
+
color: var(--color-on-success);
|
|
446
453
|
}
|
|
447
454
|
|
|
448
455
|
.astryx-badge.warning {
|
|
449
|
-
background-color:
|
|
450
|
-
color:
|
|
456
|
+
background-color: var(--astryx-theme-neutral-color-status-fill-warning);
|
|
457
|
+
color: var(--color-on-warning);
|
|
451
458
|
}
|
|
452
459
|
|
|
453
460
|
.astryx-badge.error {
|
|
454
|
-
background-color:
|
|
455
|
-
color:
|
|
461
|
+
background-color: var(--astryx-theme-neutral-color-status-fill-error);
|
|
462
|
+
color: var(--color-on-error);
|
|
456
463
|
}
|
|
457
464
|
|
|
458
465
|
.astryx-badge.red {
|
|
@@ -506,19 +513,33 @@
|
|
|
506
513
|
}
|
|
507
514
|
|
|
508
515
|
.astryx-statusdot.success {
|
|
509
|
-
background-color:
|
|
516
|
+
background-color: var(--astryx-theme-neutral-color-status-fill-success);
|
|
510
517
|
}
|
|
511
518
|
|
|
512
519
|
.astryx-statusdot.warning {
|
|
513
|
-
background-color:
|
|
520
|
+
background-color: var(--astryx-theme-neutral-color-status-fill-warning);
|
|
514
521
|
}
|
|
515
522
|
|
|
516
523
|
.astryx-statusdot.error {
|
|
517
|
-
background-color:
|
|
524
|
+
background-color: var(--astryx-theme-neutral-color-status-fill-error);
|
|
518
525
|
}
|
|
519
526
|
|
|
520
527
|
.astryx-statusdot.accent {
|
|
521
|
-
background-color:
|
|
528
|
+
background-color: var(--astryx-theme-neutral-color-status-fill-accent);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
.astryx-avatar-status-dot.success {
|
|
532
|
+
background-color: var(--astryx-theme-neutral-color-status-fill-success);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
.astryx-avatar-status-dot.error {
|
|
536
|
+
background-color: var(--astryx-theme-neutral-color-status-fill-error);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
.astryx-banner {
|
|
540
|
+
--color-neutral: var(--astryx-theme-neutral-color-on-tint-neutral);
|
|
541
|
+
--color-overlay-hover: var(--astryx-theme-neutral-color-on-tint-overlay-hover);
|
|
542
|
+
--color-overlay-pressed: var(--astryx-theme-neutral-color-on-tint-overlay-pressed);
|
|
522
543
|
}
|
|
523
544
|
|
|
524
545
|
.astryx-banner.info {
|
|
@@ -541,11 +562,28 @@
|
|
|
541
562
|
}
|
|
542
563
|
|
|
543
564
|
.astryx-banner.error {
|
|
565
|
+
--color-error-muted: var(--color-background-red);
|
|
544
566
|
--color-text-primary: var(--color-text-red);
|
|
545
567
|
--color-text-secondary: var(--color-text-red);
|
|
546
568
|
--color-error: var(--color-text-red);
|
|
547
569
|
}
|
|
548
570
|
|
|
571
|
+
.astryx-step-indicator.accent {
|
|
572
|
+
--color-accent: var(--astryx-theme-neutral-color-status-fill-accent);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
.astryx-step-indicator.success {
|
|
576
|
+
--color-success: var(--astryx-theme-neutral-color-status-fill-success);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
.astryx-step-indicator.warning {
|
|
580
|
+
--color-warning: var(--astryx-theme-neutral-color-status-fill-warning);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
.astryx-step-indicator.error {
|
|
584
|
+
--color-error: var(--astryx-theme-neutral-color-status-fill-error);
|
|
585
|
+
}
|
|
586
|
+
|
|
549
587
|
.astryx-switch {
|
|
550
588
|
--color-background-gray: var(--color-border-emphasized);
|
|
551
589
|
}
|
|
@@ -555,19 +593,19 @@
|
|
|
555
593
|
}
|
|
556
594
|
|
|
557
595
|
.astryx-progressbar.accent {
|
|
558
|
-
--color-accent:
|
|
596
|
+
--color-accent: var(--astryx-theme-neutral-color-status-fill-accent);
|
|
559
597
|
}
|
|
560
598
|
|
|
561
599
|
.astryx-progressbar.success {
|
|
562
|
-
--color-success:
|
|
600
|
+
--color-success: var(--astryx-theme-neutral-color-status-fill-success);
|
|
563
601
|
}
|
|
564
602
|
|
|
565
603
|
.astryx-progressbar.warning {
|
|
566
|
-
--color-warning:
|
|
604
|
+
--color-warning: var(--astryx-theme-neutral-color-status-fill-warning);
|
|
567
605
|
}
|
|
568
606
|
|
|
569
607
|
.astryx-progressbar.error {
|
|
570
|
-
--color-error:
|
|
608
|
+
--color-error: var(--astryx-theme-neutral-color-status-fill-error);
|
|
571
609
|
}
|
|
572
610
|
|
|
573
611
|
.astryx-card {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/theme-neutral",
|
|
3
|
-
"version": "0.5.2-canary.
|
|
3
|
+
"version": "0.5.2-canary.48cb3a4",
|
|
4
4
|
"displayName": "Neutral Theme",
|
|
5
5
|
"private": false,
|
|
6
6
|
"description": "Restrained warm grays. Minimal and quiet, so the content stays the focus.",
|
|
@@ -49,11 +49,11 @@
|
|
|
49
49
|
"lucide-react": "^1.18.0"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
|
-
"@astryxdesign/core": "0.5.2-canary.
|
|
52
|
+
"@astryxdesign/core": "0.5.2-canary.48cb3a4",
|
|
53
53
|
"react": ">=19"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
|
-
"@astryxdesign/cli": "0.5.2-canary.
|
|
56
|
+
"@astryxdesign/cli": "0.5.2-canary.48cb3a4"
|
|
57
57
|
},
|
|
58
58
|
"module": "./dist/source.mjs",
|
|
59
59
|
"scripts": {
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
import {describe, expect, it} from 'vitest';
|
|
4
|
+
import {neutralTheme} from './neutralTheme';
|
|
5
|
+
|
|
6
|
+
const statusFill = {
|
|
7
|
+
accent: 'var(--astryx-theme-neutral-color-status-fill-accent)',
|
|
8
|
+
success: 'var(--astryx-theme-neutral-color-status-fill-success)',
|
|
9
|
+
warning: 'var(--astryx-theme-neutral-color-status-fill-warning)',
|
|
10
|
+
error: 'var(--astryx-theme-neutral-color-status-fill-error)',
|
|
11
|
+
} as const;
|
|
12
|
+
|
|
13
|
+
describe('neutral theme-local status mappings', () => {
|
|
14
|
+
it('owns reusable status fills through exact Neutral-local token names', () => {
|
|
15
|
+
expect(neutralTheme.localTokens).toMatchObject({
|
|
16
|
+
'--astryx-theme-neutral-color-status-fill-accent':
|
|
17
|
+
'light-dark(#0074e2, #6d9cfe)',
|
|
18
|
+
'--astryx-theme-neutral-color-status-fill-success':
|
|
19
|
+
'light-dark(#198100, #64af4c)',
|
|
20
|
+
'--astryx-theme-neutral-color-status-fill-warning': '#ffce2f',
|
|
21
|
+
'--astryx-theme-neutral-color-status-fill-error':
|
|
22
|
+
'light-dark(#c9303a, #ff705d)',
|
|
23
|
+
});
|
|
24
|
+
expect(neutralTheme.tokens).not.toHaveProperty(
|
|
25
|
+
'--astryx-theme-neutral-color-status-fill-accent',
|
|
26
|
+
);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('shares each status fill across the components with the same role', () => {
|
|
30
|
+
expect(neutralTheme.components?.badge?.['variant:info']).toMatchObject({
|
|
31
|
+
backgroundColor: statusFill.accent,
|
|
32
|
+
});
|
|
33
|
+
expect(neutralTheme.components?.statusdot?.['variant:success']).toEqual({
|
|
34
|
+
backgroundColor: statusFill.success,
|
|
35
|
+
});
|
|
36
|
+
expect(
|
|
37
|
+
neutralTheme.components?.['avatar-status-dot']?.['variant:error'],
|
|
38
|
+
).toEqual({backgroundColor: statusFill.error});
|
|
39
|
+
expect(
|
|
40
|
+
neutralTheme.components?.['step-indicator']?.['status:warning'],
|
|
41
|
+
).toEqual({'--color-warning': statusFill.warning});
|
|
42
|
+
expect(neutralTheme.components?.progressbar?.['variant:accent']).toEqual({
|
|
43
|
+
'--color-accent': statusFill.accent,
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('does not adopt the unapproved table row-status target', () => {
|
|
48
|
+
expect(neutralTheme.components).not.toHaveProperty('table-row-status');
|
|
49
|
+
});
|
|
50
|
+
});
|
package/src/neutralTheme.ts
CHANGED
|
@@ -30,7 +30,11 @@
|
|
|
30
30
|
* Only overrides tokens that differ from the defaults.
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
defineTheme,
|
|
35
|
+
defineSyntaxTheme,
|
|
36
|
+
type TokenValue,
|
|
37
|
+
} from '@astryxdesign/core/theme';
|
|
34
38
|
import {neutralIconRegistry} from './icons';
|
|
35
39
|
|
|
36
40
|
/**
|
|
@@ -38,7 +42,7 @@ import {neutralIconRegistry} from './icons';
|
|
|
38
42
|
* stops of the categorical ramps. Same colors used by --color-icon-* tokens.
|
|
39
43
|
*/
|
|
40
44
|
const neutralSyntax = defineSyntaxTheme({
|
|
41
|
-
name: '
|
|
45
|
+
name: 'astryx-neutral',
|
|
42
46
|
tokens: {
|
|
43
47
|
keyword: ['#700084', '#efa8ff'], // purple T30/T80
|
|
44
48
|
string: ['#005600', '#a6d2a2'], // green (sat T30 / pastel T80)
|
|
@@ -59,8 +63,32 @@ const neutralSyntax = defineSyntaxTheme({
|
|
|
59
63
|
},
|
|
60
64
|
});
|
|
61
65
|
|
|
66
|
+
const neutralLocalTokens: Record<string, TokenValue> = {
|
|
67
|
+
'--astryx-theme-neutral-color-status-fill-accent': ['#0074e2', '#6d9cfe'],
|
|
68
|
+
'--astryx-theme-neutral-color-status-fill-success': ['#198100', '#64af4c'],
|
|
69
|
+
'--astryx-theme-neutral-color-status-fill-warning': '#ffce2f',
|
|
70
|
+
'--astryx-theme-neutral-color-status-fill-error': ['#c9303a', '#ff705d'],
|
|
71
|
+
'--astryx-theme-neutral-color-on-tint-neutral': ['#0a0a0a4D', '#fafafa4D'],
|
|
72
|
+
'--astryx-theme-neutral-color-on-tint-overlay-hover': [
|
|
73
|
+
'#0a0a0a1A',
|
|
74
|
+
'#fafafa1A',
|
|
75
|
+
],
|
|
76
|
+
'--astryx-theme-neutral-color-on-tint-overlay-pressed': [
|
|
77
|
+
'#0a0a0a33',
|
|
78
|
+
'#fafafa33',
|
|
79
|
+
],
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const statusFill = {
|
|
83
|
+
accent: 'var(--astryx-theme-neutral-color-status-fill-accent)',
|
|
84
|
+
success: 'var(--astryx-theme-neutral-color-status-fill-success)',
|
|
85
|
+
warning: 'var(--astryx-theme-neutral-color-status-fill-warning)',
|
|
86
|
+
error: 'var(--astryx-theme-neutral-color-status-fill-error)',
|
|
87
|
+
} as const;
|
|
88
|
+
|
|
62
89
|
export const neutralTheme = defineTheme({
|
|
63
90
|
name: 'neutral',
|
|
91
|
+
localTokens: neutralLocalTokens,
|
|
64
92
|
|
|
65
93
|
// Typography: Figtree across body, heading, and display sizes (display
|
|
66
94
|
// size tokens inherit from heading.family). Monospace stays as the
|
|
@@ -375,84 +403,35 @@ export const neutralTheme = defineTheme({
|
|
|
375
403
|
},
|
|
376
404
|
|
|
377
405
|
components: {
|
|
378
|
-
// =========================================================================
|
|
379
|
-
// Button — primary gets white text, secondary gets a border, destructive
|
|
380
|
-
// uses the OKLCH red filled treatment.
|
|
381
|
-
// =========================================================================
|
|
382
406
|
button: {
|
|
383
407
|
'variant:destructive': {
|
|
384
|
-
backgroundColor: 'var(--color-error-muted)',
|
|
385
|
-
color: 'var(--color-error)',
|
|
408
|
+
backgroundColor: 'var(--color-error-muted)',
|
|
409
|
+
color: 'var(--color-error)',
|
|
386
410
|
},
|
|
387
411
|
},
|
|
388
412
|
|
|
389
|
-
// =========================================================================
|
|
390
|
-
// Badge —
|
|
391
|
-
// Semantic (info/success/warning/error): filled saturated T50 + contrasting
|
|
392
|
-
// text (white, or dark on yellow). The filled-button rule from #2150
|
|
393
|
-
// §3 — text contrast locks the bg tone, so this stays at T50 in
|
|
394
|
-
// BOTH modes, unlike pastel surfaces which invert by mode.
|
|
395
|
-
// Categorical (blue/green/red/orange/etc.): pastel-tinted hue surface +
|
|
396
|
-
// colored text — light mode = soft T87-T90 + dark T30 text; dark mode
|
|
397
|
-
// = T20 tinted + T80 light pastel text (sources: --color-background-X
|
|
398
|
-
// and --color-text-X tokens).
|
|
399
|
-
// Neutral: light gray bg + dark text (or inverted in dark mode).
|
|
400
|
-
// =========================================================================
|
|
401
413
|
badge: {
|
|
402
|
-
// Semantic — filled saturated bg + contrasting text.
|
|
403
|
-
// Light: vivid T45-T55 from the OKLCH palette + white text
|
|
404
|
-
// (~4.5-5:1 — Material/Linear/Vercel pop).
|
|
405
|
-
// Dark : T60 stop from the dark-mode tonal palette (chroma×0.85,
|
|
406
|
-
// +5 tone-lift taper from issue #2150 §4) + DARK text.
|
|
407
|
-
// T60+white fails AA-large (~2.7:1); T60+dark hits 6.6-7:1
|
|
408
|
-
// and tames the §4 vibration. Same dark-text-on-bright-bg
|
|
409
|
-
// treatment that warning yellow uses in both modes.
|
|
410
414
|
'variant:info': {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
backgroundColor: 'light-dark(#0074e2, #6d9cfe)',
|
|
414
|
-
color: 'light-dark(#ffffff, #171717)',
|
|
415
|
+
backgroundColor: statusFill.accent,
|
|
416
|
+
color: 'var(--color-on-accent)',
|
|
415
417
|
},
|
|
416
418
|
'variant:neutral': {
|
|
417
|
-
// Mirrors the gray categorical badge — same neutral chip treatment
|
|
418
|
-
// (Neutral 200 light / semi-transparent white wash dark) sourced
|
|
419
|
-
// from the gray hue tokens, so a single change at the token layer
|
|
420
|
-
// updates both variants.
|
|
421
419
|
backgroundColor: 'var(--color-background-gray)',
|
|
422
420
|
color: 'var(--color-text-gray)',
|
|
423
421
|
},
|
|
424
422
|
'variant:success': {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
backgroundColor: 'light-dark(#198100, #64af4c)',
|
|
428
|
-
color: 'light-dark(#ffffff, #171717)',
|
|
423
|
+
backgroundColor: statusFill.success,
|
|
424
|
+
color: 'var(--color-on-success)',
|
|
429
425
|
},
|
|
430
426
|
'variant:warning': {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
// suffer from the §4 vibration concern.
|
|
434
|
-
backgroundColor: '#ffce2f',
|
|
435
|
-
color: '#171717',
|
|
427
|
+
backgroundColor: statusFill.warning,
|
|
428
|
+
color: 'var(--color-on-warning)',
|
|
436
429
|
},
|
|
437
430
|
'variant:error': {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
// large-text allowance. One tonal step down holds the hue
|
|
441
|
-
// (OKLCH H 21.9 -> 22.8, C 0.200 -> 0.189) and reaches 5.29:1.
|
|
442
|
-
// Dark : T60 stop from dark-mode tonal palette of Tailwind red-600
|
|
443
|
-
// source #dc2626 (kept on H=27 alarm-red rather than coral).
|
|
444
|
-
// Dark text on it is 6.60:1 and unchanged.
|
|
445
|
-
backgroundColor: 'light-dark(#c9303a, #ff705d)',
|
|
446
|
-
color: 'light-dark(#ffffff, #171717)',
|
|
431
|
+
backgroundColor: statusFill.error,
|
|
432
|
+
color: 'var(--color-on-error)',
|
|
447
433
|
},
|
|
448
434
|
|
|
449
|
-
// Categorical — bg + text reference the per-hue tokens, so behavior
|
|
450
|
-
// tracks the categorical palette automatically:
|
|
451
|
-
// Light: pastel T87-T90 bg + dark T30 colored text (low-key chip)
|
|
452
|
-
// Dark : tinted T20 bg + light T80 colored text (per #2150 §5,
|
|
453
|
-
// inverted from light to avoid the "pastel-in-both-modes"
|
|
454
|
-
// anti-pattern that makes locked light pastels glow on a
|
|
455
|
-
// dark body)
|
|
456
435
|
'variant:red': {
|
|
457
436
|
backgroundColor: 'var(--color-background-red)',
|
|
458
437
|
color: 'var(--color-text-red)',
|
|
@@ -495,64 +474,32 @@ export const neutralTheme = defineTheme({
|
|
|
495
474
|
},
|
|
496
475
|
},
|
|
497
476
|
|
|
498
|
-
// =========================================================================
|
|
499
|
-
// StatusDot — fill uses the SAME vivid stops as the filled semantic Badge
|
|
500
|
-
// (and ProgressBar), so a dot and its badge read as one status language.
|
|
501
|
-
//
|
|
502
|
-
// The default component maps each variant to a raw semantic token
|
|
503
|
-
// (--color-success / --color-error / --color-warning / --color-icon-
|
|
504
|
-
// secondary), which in light mode are the dark T30/T40 stops meant to
|
|
505
|
-
// sit as TEXT on a pastel surface — as a solid dot they read muddy
|
|
506
|
-
// (dark green / maroon / brown). Redirect them to the badge fills.
|
|
507
|
-
//
|
|
508
|
-
// success → badge success bg (green T45 / dark-ramp T60)
|
|
509
|
-
// warning → badge warning bg (yellow T85, same hex both modes)
|
|
510
|
-
// error → badge error bg (red T58 / dark-ramp T60)
|
|
511
|
-
// accent → badge info bg (blue T50 / dark-ramp T60) — the
|
|
512
|
-
// StatusDot "accent" is the info/attention color, so it
|
|
513
|
-
// pairs with the info badge rather than --color-accent
|
|
514
|
-
// (near-black #262626, the darkest offender).
|
|
515
|
-
//
|
|
516
|
-
// `neutral` is intentionally NOT overridden: the neutral badge bg is a
|
|
517
|
-
// near-invisible light gray (--color-background-gray #e5e5e5 / 10% white
|
|
518
|
-
// wash), fine as a large pill but unreadable as an 8px dot. It keeps the
|
|
519
|
-
// component default's visible mid-gray (--color-icon-secondary), which is
|
|
520
|
-
// not among the "too dark" cases.
|
|
521
|
-
// =========================================================================
|
|
522
477
|
statusdot: {
|
|
523
|
-
'variant:success': {backgroundColor:
|
|
524
|
-
'variant:warning': {backgroundColor:
|
|
525
|
-
'variant:error': {backgroundColor:
|
|
526
|
-
'variant:accent': {backgroundColor:
|
|
478
|
+
'variant:success': {backgroundColor: statusFill.success},
|
|
479
|
+
'variant:warning': {backgroundColor: statusFill.warning},
|
|
480
|
+
'variant:error': {backgroundColor: statusFill.error},
|
|
481
|
+
'variant:accent': {backgroundColor: statusFill.accent},
|
|
482
|
+
},
|
|
483
|
+
|
|
484
|
+
'avatar-status-dot': {
|
|
485
|
+
'variant:success': {backgroundColor: statusFill.success},
|
|
486
|
+
'variant:error': {backgroundColor: statusFill.error},
|
|
527
487
|
},
|
|
528
488
|
|
|
529
|
-
// =========================================================================
|
|
530
|
-
// Banner — sits on a hue-tinted surface with colored text/icon:
|
|
531
|
-
// Light: pastel T90 bg (pulled from --color-{X}-muted / --color-background-blue)
|
|
532
|
-
// + dark T30 colored text (--color-text-{hue}).
|
|
533
|
-
// Dark : tinted T20 bg (same tokens, dark slot) + light T80 colored text.
|
|
534
|
-
// Per #2150 §5 — large hue-tinted surfaces in dark mode invert
|
|
535
|
-
// to a deep tinted bg + light text rather than locking the
|
|
536
|
-
// light-mode pastel.
|
|
537
|
-
//
|
|
538
|
-
// The inner-header *-muted token carries the tinted background for every
|
|
539
|
-
// status, info included. A theme override that sets a plain CSS property
|
|
540
|
-
// instead lands in @layer astryx-theme, which StyleX's @layer priority4
|
|
541
|
-
// outranks, so `backgroundColor` here would silently do nothing and the
|
|
542
|
-
// info banner would paint no background at all.
|
|
543
|
-
//
|
|
544
|
-
// Status overrides reference --color-text-{hue} so text/icon colors
|
|
545
|
-
// stay in sync with the palette anchors automatically.
|
|
546
489
|
banner: {
|
|
490
|
+
base: {
|
|
491
|
+
'--color-neutral': 'var(--astryx-theme-neutral-color-on-tint-neutral)',
|
|
492
|
+
'--color-overlay-hover':
|
|
493
|
+
'var(--astryx-theme-neutral-color-on-tint-overlay-hover)',
|
|
494
|
+
'--color-overlay-pressed':
|
|
495
|
+
'var(--astryx-theme-neutral-color-on-tint-overlay-pressed)',
|
|
496
|
+
},
|
|
547
497
|
'status:info': {
|
|
548
498
|
'--color-accent-muted': 'var(--color-background-blue)',
|
|
549
499
|
'--color-text-primary': 'var(--color-text-blue)',
|
|
550
500
|
'--color-text-secondary': 'var(--color-text-blue)',
|
|
551
501
|
'--color-accent': 'var(--color-text-blue)',
|
|
552
502
|
},
|
|
553
|
-
// success/warning/error banner bgs come from --color-{X}-muted, which
|
|
554
|
-
// already carries the correct light/dark tinted values. We only need
|
|
555
|
-
// to redirect the text/icon to the palette colored stop.
|
|
556
503
|
'status:success': {
|
|
557
504
|
'--color-text-primary': 'var(--color-text-green)',
|
|
558
505
|
'--color-text-secondary': 'var(--color-text-green)',
|
|
@@ -564,29 +511,20 @@ export const neutralTheme = defineTheme({
|
|
|
564
511
|
'--color-warning': 'var(--color-text-yellow)',
|
|
565
512
|
},
|
|
566
513
|
'status:error': {
|
|
514
|
+
'--color-error-muted': 'var(--color-background-red)',
|
|
567
515
|
'--color-text-primary': 'var(--color-text-red)',
|
|
568
516
|
'--color-text-secondary': 'var(--color-text-red)',
|
|
569
517
|
'--color-error': 'var(--color-text-red)',
|
|
570
518
|
},
|
|
571
519
|
},
|
|
572
520
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
// dark T20). Verified all six combinations clear AA non-text 3:1.
|
|
580
|
-
// =========================================================================
|
|
521
|
+
'step-indicator': {
|
|
522
|
+
'status:accent': {'--color-accent': statusFill.accent},
|
|
523
|
+
'status:success': {'--color-success': statusFill.success},
|
|
524
|
+
'status:warning': {'--color-warning': statusFill.warning},
|
|
525
|
+
'status:error': {'--color-error': statusFill.error},
|
|
526
|
+
},
|
|
581
527
|
|
|
582
|
-
// =========================================================================
|
|
583
|
-
// Switch — off-state track uses the same lifted-neutral surface as the
|
|
584
|
-
// ProgressBar track (--color-border-emphasized). Aligns the two
|
|
585
|
-
// "channel-on-body" components so their off-states share one visual
|
|
586
|
-
// language: light T85 #d4d4d4 sits one step darker than the body T95
|
|
587
|
-
// bg, dark T35 #525252 sits one step lighter than the body T10. Each
|
|
588
|
-
// is a defined channel, not a wash that blends in.
|
|
589
|
-
// =========================================================================
|
|
590
528
|
switch: {
|
|
591
529
|
base: {
|
|
592
530
|
'--color-background-gray': 'var(--color-border-emphasized)',
|
|
@@ -595,46 +533,28 @@ export const neutralTheme = defineTheme({
|
|
|
595
533
|
|
|
596
534
|
progressbar: {
|
|
597
535
|
base: {
|
|
598
|
-
// Track uses --color-background-muted; override it to
|
|
599
|
-
// --color-border-emphasized (Neutral T85 #d4d4d4 in light mode) so
|
|
600
|
-
// the track is clearly darker than the body bg (Neutral T95 #f1f1f1)
|
|
601
|
-
// and reads as a defined channel rather than blending in. Dark
|
|
602
|
-
// mode inherits T35 #525252 — same one-step-lighter behavior.
|
|
603
536
|
'--color-background-muted': 'var(--color-border-emphasized)',
|
|
604
537
|
},
|
|
605
|
-
// Vivid stops match the filled semantic badge colors (info/success/
|
|
606
|
-
// warning/error variants in the badge override above). Same hex
|
|
607
|
-
// values; documented per role with palette provenance.
|
|
608
538
|
'variant:accent': {
|
|
609
|
-
|
|
610
|
-
'--color-accent': '#0074e2',
|
|
539
|
+
'--color-accent': statusFill.accent,
|
|
611
540
|
},
|
|
612
541
|
'variant:success': {
|
|
613
|
-
|
|
614
|
-
'--color-success': '#198100',
|
|
542
|
+
'--color-success': statusFill.success,
|
|
615
543
|
},
|
|
616
544
|
'variant:warning': {
|
|
617
|
-
|
|
618
|
-
'--color-warning': '#ffce2f',
|
|
545
|
+
'--color-warning': statusFill.warning,
|
|
619
546
|
},
|
|
620
547
|
'variant:error': {
|
|
621
|
-
|
|
622
|
-
'--color-error': '#c9303a',
|
|
548
|
+
'--color-error': statusFill.error,
|
|
623
549
|
},
|
|
624
550
|
},
|
|
625
551
|
|
|
626
|
-
// =========================================================================
|
|
627
|
-
// Card — tighter padding via public card padding token
|
|
628
|
-
// =========================================================================
|
|
629
552
|
card: {
|
|
630
553
|
base: {
|
|
631
554
|
padding: 'var(--spacing-3)',
|
|
632
555
|
},
|
|
633
556
|
},
|
|
634
557
|
|
|
635
|
-
// =========================================================================
|
|
636
|
-
// Section — tighter padding via public section padding token
|
|
637
|
-
// =========================================================================
|
|
638
558
|
section: {
|
|
639
559
|
base: {
|
|
640
560
|
padding: 'var(--spacing-3)',
|