@astryxdesign/theme-neutral 0.5.2 → 0.5.3
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/README.md +37 -9
- package/dist/neutral.d.ts +2 -2
- package/dist/neutral.js +174 -101
- package/dist/neutral.variants.d.ts +17 -2
- package/dist/neutralPaletteRefs.generated.d.ts +156 -0
- package/dist/neutralPaletteRefs.generated.d.ts.map +1 -0
- package/dist/neutralPalettes.d.ts +3 -0
- package/dist/neutralPalettes.d.ts.map +1 -0
- package/dist/neutralPalettes.generated.d.ts +494 -0
- package/dist/neutralPalettes.generated.d.ts.map +1 -0
- package/dist/neutralPalettes.test.d.ts +2 -0
- package/dist/neutralPalettes.test.d.ts.map +1 -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 +252 -386
- package/dist/source.mjs +256 -387
- package/dist/theme.css +163 -100
- package/package.json +5 -4
- package/palette.config.json +122 -0
- package/src/neutralPaletteRefs.generated.ts +50 -0
- package/src/neutralPalettes.generated.receipt.json +624 -0
- package/src/neutralPalettes.generated.ts +498 -0
- package/src/neutralPalettes.test.ts +67 -0
- package/src/neutralPalettes.ts +7 -0
- package/src/neutralTheme.test.ts +373 -0
- package/src/neutralTheme.ts +240 -424
package/src/neutralTheme.ts
CHANGED
|
@@ -1,66 +1,75 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
* chosen to keep each color recognizable at every tone (no red drift for
|
|
9
|
-
* orange, no blue drift for purple) and well-separated from its neighbors.
|
|
10
|
-
*
|
|
11
|
-
* Core neutral palette: #fafafa, #f5f5f5, #e5e5e5, #737373, #262626, #0a0a0a
|
|
12
|
-
*
|
|
13
|
-
* Categorical hues (OKLCH; chroma = max-in-gamut at the saturated stop):
|
|
14
|
-
* Red H=25 Orange H=65 Yellow H=90 Green H=145
|
|
15
|
-
* Teal H=180 Cyan H=215 Blue H=250 Purple H=320 Pink H=355
|
|
16
|
-
*
|
|
17
|
-
* Saturated badge stops:
|
|
18
|
-
* • Cool/medium hues sit at OKLCH L=0.48–0.50 with white text (AA+)
|
|
19
|
-
* • Bright warm hues (orange L=0.68, yellow L=0.80) use dark text
|
|
20
|
-
*
|
|
21
|
-
* Token tonal stops:
|
|
22
|
-
* bg = T90 (light) / T20 (dark)
|
|
23
|
-
* border = T80 / T30
|
|
24
|
-
* icon = T30 / T80
|
|
25
|
-
* text = T30 / T80
|
|
26
|
-
*
|
|
27
|
-
* All 9 saturated badge values pass WCAG AA against their label (>= 4.5:1);
|
|
28
|
-
* `scripts/check-badge-contrast.test.mjs` holds every theme to that.
|
|
29
|
-
*
|
|
30
|
-
* Only overrides tokens that differ from the defaults.
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
import {defineTheme, defineSyntaxTheme} from '@astryxdesign/core/theme';
|
|
3
|
+
import {
|
|
4
|
+
defineTheme,
|
|
5
|
+
defineSyntaxTheme,
|
|
6
|
+
type TokenValue,
|
|
7
|
+
} from '@astryxdesign/core/theme';
|
|
34
8
|
import {neutralIconRegistry} from './icons';
|
|
9
|
+
import {neutralPaletteRefs} from './neutralPaletteRefs.generated';
|
|
10
|
+
|
|
11
|
+
const {blue, cyan, green, neutral, orange, pink, purple, red, teal, yellow} =
|
|
12
|
+
neutralPaletteRefs;
|
|
13
|
+
const withAlpha = (color: string, alpha: string) => `${color}${alpha}`;
|
|
35
14
|
|
|
36
|
-
/**
|
|
37
|
-
* Neutral syntax palette — pulled from the OKLCH T30 (light) / T80 (dark)
|
|
38
|
-
* stops of the categorical ramps. Same colors used by --color-icon-* tokens.
|
|
39
|
-
*/
|
|
40
15
|
const neutralSyntax = defineSyntaxTheme({
|
|
41
|
-
name: '
|
|
16
|
+
name: 'astryx-neutral',
|
|
42
17
|
tokens: {
|
|
43
|
-
keyword: [
|
|
44
|
-
string: [
|
|
45
|
-
comment: [
|
|
46
|
-
number: [
|
|
47
|
-
function: [
|
|
48
|
-
type: [
|
|
49
|
-
variable: [
|
|
50
|
-
operator: [
|
|
51
|
-
constant: [
|
|
52
|
-
tag: [
|
|
53
|
-
attribute: [
|
|
54
|
-
property: [
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
punctuation: ['#6e6e6e', '#a0a0a0'], // neutral, 4.89:1 / 7.57:1
|
|
58
|
-
background: ['#fafafa', '#0a0a0a'],
|
|
18
|
+
keyword: [purple.light[30], purple.light[80]],
|
|
19
|
+
string: [green.light[30], green.light[80]],
|
|
20
|
+
comment: [neutral.light[45], neutral.dark[65]],
|
|
21
|
+
number: [orange.light[30], orange.dark[80]],
|
|
22
|
+
function: [blue.light[30], blue.dark[80]],
|
|
23
|
+
type: [purple.light[30], purple.light[80]],
|
|
24
|
+
variable: [neutral.light[5], neutral.dark[90]],
|
|
25
|
+
operator: [neutral.light[45], neutral.dark[65]],
|
|
26
|
+
constant: [orange.light[30], orange.dark[80]],
|
|
27
|
+
tag: [red.light[30], red.dark[80]],
|
|
28
|
+
attribute: [yellow.light[30], yellow.light[80]],
|
|
29
|
+
property: [teal.light[30], teal.light[80]],
|
|
30
|
+
punctuation: [neutral.light[45], neutral.dark[65]],
|
|
31
|
+
background: [neutral.light[100], neutral.dark[5]],
|
|
59
32
|
},
|
|
60
33
|
});
|
|
61
34
|
|
|
35
|
+
const neutralLocalTokens: Record<string, TokenValue> = {
|
|
36
|
+
'--astryx-theme-neutral-color-status-fill-accent': ['#0074e2', '#6d9cfe'],
|
|
37
|
+
'--astryx-theme-neutral-color-status-fill-success': ['#198100', '#64af4c'],
|
|
38
|
+
'--astryx-theme-neutral-color-status-fill-warning': '#ffce2f',
|
|
39
|
+
'--astryx-theme-neutral-color-status-fill-error': ['#c9303a', '#ff705d'],
|
|
40
|
+
'--astryx-theme-neutral-color-status-muted-accent': [
|
|
41
|
+
blue.light[85],
|
|
42
|
+
withAlpha(blue.dark[75], '3D'),
|
|
43
|
+
],
|
|
44
|
+
'--astryx-theme-neutral-color-on-tint-neutral': ['#fafafa4D', '#0a0a0a4D'],
|
|
45
|
+
'--astryx-theme-neutral-color-on-tint-overlay-hover': [
|
|
46
|
+
'#fafafa1A',
|
|
47
|
+
'#0a0a0a1A',
|
|
48
|
+
],
|
|
49
|
+
'--astryx-theme-neutral-color-on-tint-overlay-pressed': [
|
|
50
|
+
'#fafafa33',
|
|
51
|
+
'#0a0a0a33',
|
|
52
|
+
],
|
|
53
|
+
'--astryx-theme-neutral-color-destructive-overlay-hover': [
|
|
54
|
+
withAlpha(red.light[70], '0D'),
|
|
55
|
+
withAlpha(red.dark[65], '0D'),
|
|
56
|
+
],
|
|
57
|
+
'--astryx-theme-neutral-color-destructive-overlay-pressed': [
|
|
58
|
+
withAlpha(red.light[70], '1A'),
|
|
59
|
+
withAlpha(red.dark[65], '1A'),
|
|
60
|
+
],
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const statusFill = {
|
|
64
|
+
accent: 'var(--astryx-theme-neutral-color-status-fill-accent)',
|
|
65
|
+
success: 'var(--astryx-theme-neutral-color-status-fill-success)',
|
|
66
|
+
warning: 'var(--astryx-theme-neutral-color-status-fill-warning)',
|
|
67
|
+
error: 'var(--astryx-theme-neutral-color-status-fill-error)',
|
|
68
|
+
} as const;
|
|
69
|
+
|
|
62
70
|
export const neutralTheme = defineTheme({
|
|
63
71
|
name: 'neutral',
|
|
72
|
+
localTokens: neutralLocalTokens,
|
|
64
73
|
|
|
65
74
|
// Typography: Figtree across body, heading, and display sizes (display
|
|
66
75
|
// size tokens inherit from heading.family). Monospace stays as the
|
|
@@ -94,235 +103,124 @@ export const neutralTheme = defineTheme({
|
|
|
94
103
|
syntax: neutralSyntax,
|
|
95
104
|
|
|
96
105
|
tokens: {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
//
|
|
110
|
-
// Surface is the exception: it's tonally LIGHTER than body (T15) so
|
|
111
|
-
// interactive components that sit on top of body have a clear,
|
|
112
|
-
// differentiated foreground. Real consumers of --color-background-surface
|
|
113
|
-
// are: switches, radios, checkboxes, multi-selectors, dialogs, app
|
|
114
|
-
// shells, sections — all things that need to lift above the canvas.
|
|
115
|
-
//
|
|
116
|
-
// surface T15 #262626 — interactive surfaces lifted above body
|
|
117
|
-
// body T10 #1b1b1b — main canvas
|
|
118
|
-
// card T10 #1b1b1b — same as body, lifts via --shadow-low
|
|
119
|
-
// popover T10 #1b1b1b — same as body, lifts via --shadow-med
|
|
120
|
-
// muted T10 #1b1b1b — same as body
|
|
121
|
-
//
|
|
122
|
-
// Light mode keeps the standard ladder (white surfaces float on tinted
|
|
123
|
-
// body; shadows do most of the lifting):
|
|
124
|
-
// surface T100 #ffffff
|
|
125
|
-
// body T95 #f1f1f1
|
|
126
|
-
// card T100 #ffffff
|
|
127
|
-
// popover T100 #ffffff
|
|
128
|
-
// muted T95 #f1f1f1
|
|
129
|
-
//
|
|
130
|
-
// All values use the OKLCH Neutral tonal palette (chroma=0).
|
|
131
|
-
// =========================================================================
|
|
132
|
-
'--color-background-surface': ['#ffffff', '#262626'],
|
|
133
|
-
'--color-background-body': ['#f1f1f1', '#1b1b1b'],
|
|
134
|
-
'--color-background-card': ['#ffffff', '#1b1b1b'],
|
|
135
|
-
'--color-background-popover': ['#ffffff', '#1b1b1b'],
|
|
136
|
-
'--color-background-muted': ['#f1f1f1', '#1b1b1b'],
|
|
137
|
-
|
|
138
|
-
// Accent + neutral surface tints (sit alongside backgrounds)
|
|
139
|
-
'--color-accent': ['#262626', '#ebebeb'],
|
|
140
|
-
'--color-accent-muted': ['#f1f1f1', '#262626'],
|
|
141
|
-
'--color-neutral': ['#0000000F', '#FFFFFF1A'],
|
|
106
|
+
'--color-background-surface': [neutral.light[100], neutral.dark[15]],
|
|
107
|
+
'--color-background-body': [neutral.light[95], neutral.dark[10]],
|
|
108
|
+
'--color-background-card': [neutral.light[100], neutral.dark[10]],
|
|
109
|
+
'--color-background-popover': [neutral.light[100], neutral.dark[10]],
|
|
110
|
+
'--color-background-muted': [neutral.light[95], neutral.dark[10]],
|
|
111
|
+
|
|
112
|
+
'--color-accent': [neutral.light[15], neutral.dark[95]],
|
|
113
|
+
'--color-accent-muted': [neutral.light[95], neutral.dark[15]],
|
|
114
|
+
'--color-neutral': [
|
|
115
|
+
withAlpha(neutral.light[0], '0F'),
|
|
116
|
+
withAlpha(neutral.dark[100], '1A'),
|
|
117
|
+
],
|
|
142
118
|
|
|
143
119
|
// Overlays (modal scrims, hover/pressed tints)
|
|
144
|
-
'--color-overlay': [
|
|
145
|
-
|
|
146
|
-
|
|
120
|
+
'--color-overlay': [
|
|
121
|
+
withAlpha(neutral.light[0], '80'),
|
|
122
|
+
withAlpha(neutral.dark[0], 'CC'),
|
|
123
|
+
],
|
|
124
|
+
'--color-overlay-hover': [
|
|
125
|
+
withAlpha(neutral.light[0], '0D'),
|
|
126
|
+
withAlpha(neutral.dark[100], '0D'),
|
|
127
|
+
],
|
|
128
|
+
'--color-overlay-pressed': [
|
|
129
|
+
withAlpha(neutral.light[0], '1A'),
|
|
130
|
+
withAlpha(neutral.dark[100], '1A'),
|
|
131
|
+
],
|
|
147
132
|
|
|
148
133
|
// Text
|
|
149
|
-
'--color-text-primary': [
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
'--color-
|
|
154
|
-
'--color-
|
|
155
|
-
'--color-
|
|
156
|
-
'--color-on-
|
|
157
|
-
'--color-on-
|
|
158
|
-
|
|
159
|
-
'--color-on-accent': ['#ffffff', '#171717'],
|
|
160
|
-
'--color-on-success': ['#ffffff', '#171717'],
|
|
161
|
-
'--color-on-error': ['#ffffff', '#171717'],
|
|
162
|
-
'--color-on-warning': '#171717',
|
|
134
|
+
'--color-text-primary': [neutral.light[5], neutral.dark[100]],
|
|
135
|
+
'--color-text-secondary': [neutral.light[35], neutral.dark[65]],
|
|
136
|
+
'--color-text-disabled': [neutral.light[65], neutral.dark[35]],
|
|
137
|
+
'--color-text-accent': [neutral.light[15], neutral.dark[95]],
|
|
138
|
+
'--color-on-dark': neutral.light[100],
|
|
139
|
+
'--color-on-light': neutral.light[5],
|
|
140
|
+
'--color-on-accent': [neutral.light[100], neutral.dark[5]],
|
|
141
|
+
'--color-on-success': [neutral.light[100], neutral.dark[5]],
|
|
142
|
+
'--color-on-error': [neutral.light[100], neutral.dark[5]],
|
|
143
|
+
'--color-on-warning': neutral.light[5],
|
|
163
144
|
|
|
164
145
|
// Icon
|
|
165
|
-
'--color-icon-accent': [
|
|
166
|
-
'--color-icon-primary': [
|
|
167
|
-
'--color-icon-secondary': [
|
|
168
|
-
'--color-icon-disabled': [
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
// dark = hue-tinted alpha overlay (T70 stop @ 24%)
|
|
186
|
-
// Used by banner bg, status-input message bg,
|
|
187
|
-
// destructive button bg. Dark mode uses an alpha
|
|
188
|
-
// overlay rather than a solid T20 tinted bg so
|
|
189
|
-
// the surface composes onto whatever sits behind
|
|
190
|
-
// it (body, card, popover) rather than reading
|
|
191
|
-
// as a hard colored panel.
|
|
192
|
-
//
|
|
193
|
-
// 24% alpha = '3D' suffix. Hue values match --color-icon-{X} dark
|
|
194
|
-
// slots (palette T70). Composited onto body #1b1b1b, the effective
|
|
195
|
-
// bg luminance hits ~1.65-1.70:1 vs body — visible colored surface
|
|
196
|
-
// without the heaviness of a solid T20 panel.
|
|
197
|
-
'--color-success': ['#007004', '#9fe59b'],
|
|
198
|
-
'--color-error': ['#a50c25', '#ffc6c1'],
|
|
199
|
-
'--color-warning': ['#745b00', '#fdcf4f'],
|
|
200
|
-
'--color-success-muted': ['#c5e5c0', '#84c9803D'],
|
|
201
|
-
'--color-error-muted': ['#facecb', '#ff9e973D'],
|
|
202
|
-
'--color-warning-muted': ['#f8da9d', '#deb4333D'],
|
|
203
|
-
|
|
204
|
-
// Border
|
|
205
|
-
'--color-border': ['#00000014', '#FFFFFF1A'],
|
|
206
|
-
'--color-border-emphasized': ['#d4d4d4', '#525252'],
|
|
146
|
+
'--color-icon-accent': [neutral.light[15], neutral.dark[95]],
|
|
147
|
+
'--color-icon-primary': [neutral.light[5], neutral.dark[100]],
|
|
148
|
+
'--color-icon-secondary': [neutral.light[50], neutral.dark[65]],
|
|
149
|
+
'--color-icon-disabled': [neutral.light[65], neutral.dark[35]],
|
|
150
|
+
|
|
151
|
+
'--color-success': [green.light[40], green.light[80]],
|
|
152
|
+
'--color-error': [red.light[35], red.dark[85]],
|
|
153
|
+
'--color-warning': [yellow.light[40], yellow.light[85]],
|
|
154
|
+
'--color-success-muted': [green.dark[85], withAlpha(green.light[75], '3D')],
|
|
155
|
+
'--color-error-muted': [red.light[85], withAlpha(red.dark[75], '3D')],
|
|
156
|
+
'--color-warning-muted': [
|
|
157
|
+
yellow.dark[90],
|
|
158
|
+
withAlpha(yellow.light[75], '3D'),
|
|
159
|
+
],
|
|
160
|
+
|
|
161
|
+
'--color-border': [
|
|
162
|
+
withAlpha(neutral.light[0], '14'),
|
|
163
|
+
withAlpha(neutral.dark[100], '1A'),
|
|
164
|
+
],
|
|
165
|
+
'--color-border-emphasized': [neutral.light[85], neutral.dark[35]],
|
|
207
166
|
|
|
208
167
|
// Effects
|
|
209
|
-
'--color-skeleton': [
|
|
210
|
-
'--color-shadow': [
|
|
168
|
+
'--color-skeleton': [neutral.light[95], neutral.dark[35]],
|
|
169
|
+
'--color-shadow': [
|
|
170
|
+
withAlpha(neutral.light[0], '1A'),
|
|
171
|
+
withAlpha(neutral.dark[0], '4D'),
|
|
172
|
+
],
|
|
211
173
|
'--color-tint-hover': ['black', 'white'],
|
|
212
174
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
'--color-
|
|
246
|
-
'--color-
|
|
247
|
-
|
|
248
|
-
'--color-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
'--color-
|
|
252
|
-
|
|
253
|
-
'--color-
|
|
254
|
-
'--color-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
// to red/blue's perceived brightness without losing yellow identity.
|
|
263
|
-
// Dark-mode comes from the canonical H=90 ramp for tonal-palette
|
|
264
|
-
// consistency.
|
|
265
|
-
'--color-background-yellow': ['#f8da9d', '#deb4333D'],
|
|
266
|
-
'--color-border-yellow': ['#e4c279', '#c0990e'],
|
|
267
|
-
'--color-icon-yellow': ['#584400', '#deb433'],
|
|
268
|
-
'--color-text-yellow': ['#584400', '#fdcf4f'],
|
|
269
|
-
|
|
270
|
-
// Green H=144 — source #358a3a
|
|
271
|
-
'--color-background-green': ['#c5e5c0', '#84c9803D'],
|
|
272
|
-
'--color-border-green': ['#b2d1ac', '#69ad67'],
|
|
273
|
-
'--color-icon-green': ['#0c5700', '#84c980'],
|
|
274
|
-
'--color-text-green': ['#0c5700', '#9fe59b'],
|
|
275
|
-
|
|
276
|
-
// Teal H=180 — source #0c7365
|
|
277
|
-
// Light pastel uses L=0.87 C=0.065 (a step darker + less chroma than
|
|
278
|
-
// the L=0.888 C=0.07 used by other hues) to compensate for the
|
|
279
|
-
// green-cyan luminance overshoot — at the same OKLCH L, teal/cyan read
|
|
280
|
-
// ~5% brighter than red/blue because the eye's luminance response
|
|
281
|
-
// peaks in this band. Dropping L+C brings perceived brightness in
|
|
282
|
-
// line with the rest of the palette without losing hue identity.
|
|
283
|
-
'--color-background-teal': ['#a5e3d6', '#7ec6b83D'],
|
|
284
|
-
'--color-border-teal': ['#94d6c8', '#63ab9d'],
|
|
285
|
-
'--color-icon-teal': ['#005348', '#7ec6b8'],
|
|
286
|
-
'--color-text-teal': ['#005348', '#99e2d3'],
|
|
287
|
-
|
|
288
|
-
// Cyan H=215 — source #0c6f82
|
|
289
|
-
// Same L=0.87 C=0.065 pastel as teal (luminance overshoot compensation).
|
|
290
|
-
'--color-background-cyan': ['#a3e0ef', '#83c2d43D'],
|
|
291
|
-
'--color-border-cyan': ['#91d3e3', '#67a7b8'],
|
|
292
|
-
'--color-icon-cyan': ['#00505f', '#83c2d4'],
|
|
293
|
-
'--color-text-cyan': ['#00505f', '#9edef0'],
|
|
294
|
-
|
|
295
|
-
// Blue H=255 — source #0074e2
|
|
296
|
-
// T50 #0074e2 reserved for filled Info badge / progressbar / inset hover.
|
|
297
|
-
'--color-background-blue': ['#c4ddfb', '#9eb7ff3D'],
|
|
298
|
-
'--color-border-blue': ['#b1c9e7', '#6d9cfe'],
|
|
299
|
-
'--color-icon-blue': ['#00458c', '#9eb7ff'],
|
|
300
|
-
'--color-text-blue': ['#00458c', '#c7d3ff'],
|
|
301
|
-
|
|
302
|
-
// Purple H=320 — source #980fb2
|
|
303
|
-
'--color-background-purple': ['#eccef3', '#f297ff3D'],
|
|
304
|
-
'--color-border-purple': ['#d8bbdf', '#dd74f0'],
|
|
305
|
-
'--color-icon-purple': ['#700084', '#f297ff'],
|
|
306
|
-
'--color-text-purple': ['#700084', '#fac1ff'],
|
|
307
|
-
|
|
308
|
-
// Pink H=355 — source #b10e69
|
|
309
|
-
'--color-background-pink': ['#fccadc', '#ff99c33D'],
|
|
310
|
-
'--color-border-pink': ['#e7b7c8', '#f273aa'],
|
|
311
|
-
'--color-icon-pink': ['#83004b', '#ff99c3'],
|
|
312
|
-
'--color-text-pink': ['#83004b', '#ffc3da'],
|
|
313
|
-
|
|
314
|
-
// Gray (categorical neutral, chroma 0)
|
|
315
|
-
// Light: #e5e5e5 (Neutral 200) so it's visibly distinct from the
|
|
316
|
-
// lighter body / muted surface (both #f5f5f5).
|
|
317
|
-
// Dark : var(--color-neutral) — semi-transparent white wash
|
|
318
|
-
// (#FFFFFF1A, 10%). Matches the same treatment the gray
|
|
319
|
-
// badge uses; clearly distinct from the body T10 #1b1b1b
|
|
320
|
-
// while staying chroma-0 neutral. Solid T15 #1c1c1c was
|
|
321
|
-
// indistinguishable from --color-background-muted.
|
|
322
|
-
'--color-background-gray': ['#e5e5e5', 'var(--color-neutral)'],
|
|
323
|
-
'--color-border-gray': ['#d4d4d4', '#262626'],
|
|
324
|
-
'--color-icon-gray': ['#525252', '#a3a3a3'],
|
|
325
|
-
'--color-text-gray': ['#262626', '#e5e5e5'],
|
|
175
|
+
'--color-background-red': [red.light[85], red.dark[25]],
|
|
176
|
+
'--color-border-red': [red.light[80], red.light[65]],
|
|
177
|
+
'--color-icon-red': [red.light[30], red.dark[75]],
|
|
178
|
+
'--color-text-red': [red.light[30], red.dark[80]],
|
|
179
|
+
|
|
180
|
+
'--color-background-orange': [orange.light[85], orange.dark[25]],
|
|
181
|
+
'--color-border-orange': [orange.light[85], orange.dark[65]],
|
|
182
|
+
'--color-icon-orange': [orange.light[30], orange.light[75]],
|
|
183
|
+
'--color-text-orange': [orange.light[30], orange.dark[80]],
|
|
184
|
+
|
|
185
|
+
'--color-background-yellow': [yellow.dark[90], yellow.dark[25]],
|
|
186
|
+
'--color-border-yellow': [yellow.dark[80], yellow.light[65]],
|
|
187
|
+
'--color-icon-yellow': [yellow.light[30], yellow.light[75]],
|
|
188
|
+
'--color-text-yellow': [yellow.light[30], yellow.light[80]],
|
|
189
|
+
|
|
190
|
+
'--color-background-green': [green.dark[85], green.dark[25]],
|
|
191
|
+
'--color-border-green': [green.dark[80], green.light[65]],
|
|
192
|
+
'--color-icon-green': [green.light[30], green.light[75]],
|
|
193
|
+
'--color-text-green': [green.light[30], green.light[75]],
|
|
194
|
+
|
|
195
|
+
'--color-background-teal': [teal.light[85], teal.dark[25]],
|
|
196
|
+
'--color-border-teal': [teal.light[80], teal.dark[65]],
|
|
197
|
+
'--color-icon-teal': [teal.light[30], teal.dark[75]],
|
|
198
|
+
'--color-text-teal': [teal.light[30], teal.light[80]],
|
|
199
|
+
|
|
200
|
+
'--color-background-cyan': [cyan.dark[85], cyan.dark[25]],
|
|
201
|
+
'--color-border-cyan': [cyan.dark[80], cyan.dark[65]],
|
|
202
|
+
'--color-icon-cyan': [cyan.light[30], cyan.dark[75]],
|
|
203
|
+
'--color-text-cyan': [cyan.light[30], cyan.dark[80]],
|
|
204
|
+
|
|
205
|
+
'--color-background-blue': [blue.light[85], blue.dark[25]],
|
|
206
|
+
'--color-border-blue': [blue.light[80], blue.dark[65]],
|
|
207
|
+
'--color-icon-blue': [blue.light[30], blue.dark[75]],
|
|
208
|
+
'--color-text-blue': [blue.light[30], blue.dark[80]],
|
|
209
|
+
|
|
210
|
+
'--color-background-purple': [purple.light[90], purple.dark[25]],
|
|
211
|
+
'--color-border-purple': [purple.light[85], purple.light[70]],
|
|
212
|
+
'--color-icon-purple': [purple.light[30], purple.light[75]],
|
|
213
|
+
'--color-text-purple': [purple.light[30], purple.dark[80]],
|
|
214
|
+
|
|
215
|
+
'--color-background-pink': [pink.light[85], pink.dark[25]],
|
|
216
|
+
'--color-border-pink': [pink.light[85], pink.light[70]],
|
|
217
|
+
'--color-icon-pink': [pink.light[30], pink.dark[75]],
|
|
218
|
+
'--color-text-pink': [pink.light[30], pink.dark[80]],
|
|
219
|
+
|
|
220
|
+
'--color-background-gray': [neutral.light[90], neutral.dark[20]],
|
|
221
|
+
'--color-border-gray': [neutral.light[85], neutral.dark[15]],
|
|
222
|
+
'--color-icon-gray': [neutral.light[35], neutral.dark[65]],
|
|
223
|
+
'--color-text-gray': [neutral.light[15], neutral.dark[85]],
|
|
326
224
|
|
|
327
225
|
// =========================================================================
|
|
328
226
|
// Radius — slightly larger than default (kept as-is)
|
|
@@ -367,92 +265,47 @@ export const neutralTheme = defineTheme({
|
|
|
367
265
|
'0 4px 6px light-dark(oklch(0 0 0 / 10%), oklch(0 0 0 / 50%)), ' +
|
|
368
266
|
'0 12px 24px light-dark(oklch(0 0 0 / 15%), oklch(0 0 0 / 70%)), ' +
|
|
369
267
|
'inset 0 0 0 1px light-dark(transparent, oklch(1 0 0 / 15%))',
|
|
370
|
-
'--shadow-inset-hover':
|
|
371
|
-
'--shadow-inset-selected':
|
|
372
|
-
'--shadow-inset-success':
|
|
373
|
-
'--shadow-inset-warning':
|
|
374
|
-
'--shadow-inset-error':
|
|
268
|
+
'--shadow-inset-hover': `inset 0px 0px 0px 2px ${withAlpha(blue.light[50], '4D')}`,
|
|
269
|
+
'--shadow-inset-selected': `inset 0px 0px 0px 2px ${withAlpha(blue.light[50], '80')}`,
|
|
270
|
+
'--shadow-inset-success': `inset 0px 0px 0px 2px ${withAlpha(green.light[45], '4D')}`,
|
|
271
|
+
'--shadow-inset-warning': `inset 0px 0px 0px 2px ${withAlpha(yellow.light[85], '4D')}`,
|
|
272
|
+
'--shadow-inset-error': `inset 0px 0px 0px 2px ${withAlpha(red.light[55], '4D')}`,
|
|
375
273
|
},
|
|
376
274
|
|
|
377
275
|
components: {
|
|
378
|
-
// =========================================================================
|
|
379
|
-
// Button — primary gets white text, secondary gets a border, destructive
|
|
380
|
-
// uses the OKLCH red filled treatment.
|
|
381
|
-
// =========================================================================
|
|
382
276
|
button: {
|
|
383
277
|
'variant:destructive': {
|
|
384
|
-
backgroundColor: 'var(--color-error-muted)',
|
|
385
|
-
color: 'var(--color-error)',
|
|
278
|
+
backgroundColor: 'var(--color-error-muted)',
|
|
279
|
+
color: 'var(--color-error)',
|
|
280
|
+
'--color-overlay-hover':
|
|
281
|
+
'var(--astryx-theme-neutral-color-destructive-overlay-hover)',
|
|
282
|
+
'--color-overlay-pressed':
|
|
283
|
+
'var(--astryx-theme-neutral-color-destructive-overlay-pressed)',
|
|
386
284
|
},
|
|
387
285
|
},
|
|
388
286
|
|
|
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
287
|
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
288
|
'variant:info': {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
backgroundColor: 'light-dark(#0074e2, #6d9cfe)',
|
|
414
|
-
color: 'light-dark(#ffffff, #171717)',
|
|
289
|
+
backgroundColor: statusFill.accent,
|
|
290
|
+
color: 'var(--color-on-accent)',
|
|
415
291
|
},
|
|
416
292
|
'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
293
|
backgroundColor: 'var(--color-background-gray)',
|
|
422
294
|
color: 'var(--color-text-gray)',
|
|
423
295
|
},
|
|
424
296
|
'variant:success': {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
backgroundColor: 'light-dark(#198100, #64af4c)',
|
|
428
|
-
color: 'light-dark(#ffffff, #171717)',
|
|
297
|
+
backgroundColor: statusFill.success,
|
|
298
|
+
color: 'var(--color-on-success)',
|
|
429
299
|
},
|
|
430
300
|
'variant:warning': {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
// suffer from the §4 vibration concern.
|
|
434
|
-
backgroundColor: '#ffce2f',
|
|
435
|
-
color: '#171717',
|
|
301
|
+
backgroundColor: statusFill.warning,
|
|
302
|
+
color: 'var(--color-on-warning)',
|
|
436
303
|
},
|
|
437
304
|
'variant:error': {
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
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)',
|
|
447
|
-
},
|
|
448
|
-
|
|
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)
|
|
305
|
+
backgroundColor: statusFill.error,
|
|
306
|
+
color: 'var(--color-on-error)',
|
|
307
|
+
},
|
|
308
|
+
|
|
456
309
|
'variant:red': {
|
|
457
310
|
backgroundColor: 'var(--color-background-red)',
|
|
458
311
|
color: 'var(--color-text-red)',
|
|
@@ -495,64 +348,55 @@ export const neutralTheme = defineTheme({
|
|
|
495
348
|
},
|
|
496
349
|
},
|
|
497
350
|
|
|
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
351
|
statusdot: {
|
|
523
|
-
'variant:success': {backgroundColor:
|
|
524
|
-
'variant:warning': {backgroundColor:
|
|
525
|
-
'variant:error': {backgroundColor:
|
|
526
|
-
'variant:accent': {backgroundColor:
|
|
352
|
+
'variant:success': {backgroundColor: statusFill.success},
|
|
353
|
+
'variant:warning': {backgroundColor: statusFill.warning},
|
|
354
|
+
'variant:error': {backgroundColor: statusFill.error},
|
|
355
|
+
'variant:accent': {backgroundColor: statusFill.accent},
|
|
356
|
+
},
|
|
357
|
+
|
|
358
|
+
'avatar-status-dot': {
|
|
359
|
+
'variant:success': {backgroundColor: statusFill.success},
|
|
360
|
+
'variant:error': {backgroundColor: statusFill.error},
|
|
361
|
+
},
|
|
362
|
+
|
|
363
|
+
// Give the Neutral segmented control a roomier inset without changing its
|
|
364
|
+
// outside height. The selected item stays flat against the tinted track.
|
|
365
|
+
'segmented-control': {
|
|
366
|
+
base: {
|
|
367
|
+
padding: 'var(--spacing-1)',
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
'segmented-control-item': {
|
|
371
|
+
'size:sm': {
|
|
372
|
+
height: 'calc(var(--size-element-sm) - 8px)',
|
|
373
|
+
},
|
|
374
|
+
'size:md': {
|
|
375
|
+
height: 'calc(var(--size-element-md) - 8px)',
|
|
376
|
+
},
|
|
377
|
+
'size:lg': {
|
|
378
|
+
height: 'calc(var(--size-element-lg) - 8px)',
|
|
379
|
+
},
|
|
380
|
+
selected: {
|
|
381
|
+
boxShadow: 'none',
|
|
382
|
+
},
|
|
527
383
|
},
|
|
528
384
|
|
|
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
385
|
banner: {
|
|
386
|
+
base: {
|
|
387
|
+
'--color-neutral': 'var(--astryx-theme-neutral-color-on-tint-neutral)',
|
|
388
|
+
'--color-overlay-hover':
|
|
389
|
+
'var(--astryx-theme-neutral-color-on-tint-overlay-hover)',
|
|
390
|
+
'--color-overlay-pressed':
|
|
391
|
+
'var(--astryx-theme-neutral-color-on-tint-overlay-pressed)',
|
|
392
|
+
},
|
|
547
393
|
'status:info': {
|
|
548
|
-
'--color-accent-muted':
|
|
394
|
+
'--color-accent-muted':
|
|
395
|
+
'var(--astryx-theme-neutral-color-status-muted-accent)',
|
|
549
396
|
'--color-text-primary': 'var(--color-text-blue)',
|
|
550
397
|
'--color-text-secondary': 'var(--color-text-blue)',
|
|
551
398
|
'--color-accent': 'var(--color-text-blue)',
|
|
552
399
|
},
|
|
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
400
|
'status:success': {
|
|
557
401
|
'--color-text-primary': 'var(--color-text-green)',
|
|
558
402
|
'--color-text-secondary': 'var(--color-text-green)',
|
|
@@ -570,23 +414,13 @@ export const neutralTheme = defineTheme({
|
|
|
570
414
|
},
|
|
571
415
|
},
|
|
572
416
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
// dark T20). Verified all six combinations clear AA non-text 3:1.
|
|
580
|
-
// =========================================================================
|
|
417
|
+
'step-indicator': {
|
|
418
|
+
'status:accent': {'--color-accent': statusFill.accent},
|
|
419
|
+
'status:success': {'--color-success': statusFill.success},
|
|
420
|
+
'status:warning': {'--color-warning': statusFill.warning},
|
|
421
|
+
'status:error': {'--color-error': statusFill.error},
|
|
422
|
+
},
|
|
581
423
|
|
|
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
424
|
switch: {
|
|
591
425
|
base: {
|
|
592
426
|
'--color-background-gray': 'var(--color-border-emphasized)',
|
|
@@ -595,46 +429,28 @@ export const neutralTheme = defineTheme({
|
|
|
595
429
|
|
|
596
430
|
progressbar: {
|
|
597
431
|
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
432
|
'--color-background-muted': 'var(--color-border-emphasized)',
|
|
604
433
|
},
|
|
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
434
|
'variant:accent': {
|
|
609
|
-
|
|
610
|
-
'--color-accent': '#0074e2',
|
|
435
|
+
'--color-accent': statusFill.accent,
|
|
611
436
|
},
|
|
612
437
|
'variant:success': {
|
|
613
|
-
|
|
614
|
-
'--color-success': '#198100',
|
|
438
|
+
'--color-success': statusFill.success,
|
|
615
439
|
},
|
|
616
440
|
'variant:warning': {
|
|
617
|
-
|
|
618
|
-
'--color-warning': '#ffce2f',
|
|
441
|
+
'--color-warning': statusFill.warning,
|
|
619
442
|
},
|
|
620
443
|
'variant:error': {
|
|
621
|
-
|
|
622
|
-
'--color-error': '#c9303a',
|
|
444
|
+
'--color-error': statusFill.error,
|
|
623
445
|
},
|
|
624
446
|
},
|
|
625
447
|
|
|
626
|
-
// =========================================================================
|
|
627
|
-
// Card — tighter padding via public card padding token
|
|
628
|
-
// =========================================================================
|
|
629
448
|
card: {
|
|
630
449
|
base: {
|
|
631
450
|
padding: 'var(--spacing-3)',
|
|
632
451
|
},
|
|
633
452
|
},
|
|
634
453
|
|
|
635
|
-
// =========================================================================
|
|
636
|
-
// Section — tighter padding via public section padding token
|
|
637
|
-
// =========================================================================
|
|
638
454
|
section: {
|
|
639
455
|
base: {
|
|
640
456
|
padding: 'var(--spacing-3)',
|