@hattisar/design-tokens 0.3.5 → 0.3.6
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/package.json +1 -1
- package/tokens.css +217 -108
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hattisar/design-tokens",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.6",
|
|
4
4
|
"description": "Shared design tokens (colors, type, radius, spacing, motion) and base primitives (reset, buttons, inputs, cards, modals, auth shell) for every Hattisar frontend.",
|
|
5
5
|
"main": "tokens.css",
|
|
6
6
|
"files": [
|
package/tokens.css
CHANGED
|
@@ -32,22 +32,30 @@
|
|
|
32
32
|
>=4.5:1) and for a real 3-step surface ladder — surface-tint used
|
|
33
33
|
to equal background-global exactly, which meant hover states had
|
|
34
34
|
no visible target to land on. */
|
|
35
|
-
--color-background-global: #
|
|
35
|
+
--color-background-global: #f7f8fa;
|
|
36
36
|
--color-background-card: #ffffff;
|
|
37
|
-
--color-surface-tint: #
|
|
38
|
-
--color-input: #
|
|
39
|
-
--color-text-primary: #
|
|
37
|
+
--color-surface-tint: #f0f2f6;
|
|
38
|
+
--color-input: #ffffff;
|
|
39
|
+
--color-text-primary: #10141a;
|
|
40
40
|
/* ~6.4:1 on card */
|
|
41
|
-
--color-text-secondary: #
|
|
41
|
+
--color-text-secondary: #4b5563;
|
|
42
42
|
/* ~4.6:1 on card — was #8f99a8 at 2.9:1, failing AA body text */
|
|
43
|
-
--color-text-muted: #
|
|
43
|
+
--color-text-muted: #5f6874;
|
|
44
44
|
/* Derived from text-primary's own RGB (28,33,39) rather than a
|
|
45
45
|
separately maintained literal, so border and text can never drift
|
|
46
46
|
apart the way they used to (border was rgb(17,20,24), a different
|
|
47
47
|
dark from text-primary's rgb(28,33,39)). */
|
|
48
|
-
--color-border: rgba(
|
|
49
|
-
--color-border-strong: rgba(
|
|
48
|
+
--color-border: rgba(16, 20, 26, 0.09);
|
|
49
|
+
--color-border-strong: rgba(16, 20, 26, 0.16);
|
|
50
50
|
--color-hover: var(--color-surface-tint);
|
|
51
|
+
--color-focus-ring: rgba(233, 235, 239, 0.2);
|
|
52
|
+
--color-focus-ring-danger: rgba(255, 138, 156, 0.24);
|
|
53
|
+
/* The ring drawn around a focused control. Ink at low alpha rather
|
|
54
|
+
than a saturated blue: this palette has no brand hue, and a blue
|
|
55
|
+
ring here would be the one place colour appeared without meaning
|
|
56
|
+
anything — the ten status tiers are what colour is for. */
|
|
57
|
+
--color-focus-ring: rgba(16, 20, 26, 0.14);
|
|
58
|
+
--color-focus-ring-danger: rgba(199, 10, 36, 0.2);
|
|
51
59
|
|
|
52
60
|
/* Named accents — the same 8 hues most of the status tiers below
|
|
53
61
|
already used, promoted into reusable tokens instead of each tier repeating
|
|
@@ -69,7 +77,7 @@
|
|
|
69
77
|
--accent-amber: #8a6600;
|
|
70
78
|
--accent-bronze: #a35200;
|
|
71
79
|
--accent-blue: #005bd3;
|
|
72
|
-
--accent-cyan: #
|
|
80
|
+
--accent-cyan: #0a6d99;
|
|
73
81
|
--accent-mint: #047b5d;
|
|
74
82
|
--accent-fuchsia: #984062;
|
|
75
83
|
|
|
@@ -119,28 +127,28 @@
|
|
|
119
127
|
computed. See StatusBadge.tsx for the status keyword -> tier
|
|
120
128
|
mapping — ten distinct hues, deliberately no collisions. */
|
|
121
129
|
--status-draft-text: var(--accent-slate);
|
|
122
|
-
--status-draft-bg: #
|
|
130
|
+
--status-draft-bg: #eef1f6;
|
|
123
131
|
--status-backordered-text: var(--accent-violet);
|
|
124
|
-
--status-backordered-bg: #
|
|
132
|
+
--status-backordered-bg: #f3ecfd;
|
|
125
133
|
--status-low-stock-text: var(--accent-amber);
|
|
126
|
-
--status-low-stock-bg: #
|
|
134
|
+
--status-low-stock-bg: #fff1d9;
|
|
127
135
|
/* Near-empty stock — more urgent than low-stock's reorder-point
|
|
128
136
|
nudge, not yet fully out. Bg sits between low-stock's punchy
|
|
129
137
|
amber-gold and out-of-stock's pale peach; text is --color-critical. */
|
|
130
138
|
--status-critical-stock-text: var(--color-critical);
|
|
131
|
-
--status-critical-stock-bg: #
|
|
139
|
+
--status-critical-stock-bg: #ffe7d1;
|
|
132
140
|
--status-out-of-stock-text: var(--accent-bronze);
|
|
133
|
-
--status-out-of-stock-bg: #
|
|
141
|
+
--status-out-of-stock-bg: #ffeee4;
|
|
134
142
|
--status-partially-fulfilled-text: var(--accent-blue);
|
|
135
|
-
--status-partially-fulfilled-bg: #
|
|
143
|
+
--status-partially-fulfilled-bg: #e8f1fe;
|
|
136
144
|
--status-shipped-text: var(--accent-cyan);
|
|
137
|
-
--status-shipped-bg: #
|
|
145
|
+
--status-shipped-bg: #ddf0fb;
|
|
138
146
|
--status-delivered-text: var(--accent-mint);
|
|
139
|
-
--status-delivered-bg: #
|
|
147
|
+
--status-delivered-bg: #e1f5e6;
|
|
140
148
|
--status-cancelled-text: var(--accent-fuchsia);
|
|
141
|
-
--status-cancelled-bg: #
|
|
149
|
+
--status-cancelled-bg: #fde7ee;
|
|
142
150
|
--status-overdue-text: var(--color-danger);
|
|
143
|
-
--status-overdue-bg: #
|
|
151
|
+
--status-overdue-bg: #feebec;
|
|
144
152
|
|
|
145
153
|
/* Page-level warning/danger/success banners — distinct from the row
|
|
146
154
|
badges above: a border plus a matching soft fill, no separate text
|
|
@@ -149,11 +157,11 @@
|
|
|
149
157
|
repeating their own literal hex) so they can never drift out of
|
|
150
158
|
sync the way they briefly did here. Success intentionally doesn't:
|
|
151
159
|
it was never meant to mirror --color-success, it's its own shade. */
|
|
152
|
-
--banner-warning-bg: #
|
|
160
|
+
--banner-warning-bg: #fff3e6;
|
|
153
161
|
--banner-warning-border: var(--color-warning);
|
|
154
|
-
--banner-danger-bg: #
|
|
162
|
+
--banner-danger-bg: #fdeaed;
|
|
155
163
|
--banner-danger-border: var(--color-danger);
|
|
156
|
-
--banner-success-bg: #
|
|
164
|
+
--banner-success-bg: #e2f7e6;
|
|
157
165
|
--banner-success-border: var(--color-success);
|
|
158
166
|
|
|
159
167
|
/* A restrained scale, not flat — every step deliberately small, since
|
|
@@ -165,17 +173,28 @@
|
|
|
165
173
|
remove-buttons inside chips, the settings toggle switch) — never
|
|
166
174
|
applied to a badge/tag/chip just for a rounder look; those use
|
|
167
175
|
--radius-sm like everything else (see .status-badge and friends). */
|
|
168
|
-
--radius-xs:
|
|
169
|
-
--radius-sm:
|
|
170
|
-
--radius-md:
|
|
171
|
-
--radius-lg:
|
|
172
|
-
--radius-xl:
|
|
176
|
+
--radius-xs: 5px;
|
|
177
|
+
--radius-sm: 7px;
|
|
178
|
+
--radius-md: 10px;
|
|
179
|
+
--radius-lg: 13px;
|
|
180
|
+
--radius-xl: 16px;
|
|
173
181
|
--radius-full: 9999px;
|
|
174
182
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
183
|
+
/* Depth, deliberately restrained. These were all `none`, which left
|
|
184
|
+
every surface separated by border alone — correct, legible, and
|
|
185
|
+
completely flat: a card, a dropdown and a modal all sat on the page
|
|
186
|
+
at exactly the same height. The 32 places already reaching for
|
|
187
|
+
these tokens were asking for a hierarchy the tokens declined to
|
|
188
|
+
give them.
|
|
189
|
+
Four tiers, each tied to a job: xs for a resting control, sm for a
|
|
190
|
+
card, md for anything that floats over the page (dropdowns,
|
|
191
|
+
popovers), lg for a modal. Tinted with the ink colour rather than
|
|
192
|
+
pure black so a shadow reads as the surface's own shade instead of
|
|
193
|
+
a grey smudge. */
|
|
194
|
+
--shadow-xs: 0 1px 2px rgba(16, 20, 26, 0.04);
|
|
195
|
+
--shadow-sm: 0 1px 2px rgba(16, 20, 26, 0.05), 0 2px 8px -3px rgba(16, 20, 26, 0.08);
|
|
196
|
+
--shadow-md: 0 2px 4px -1px rgba(16, 20, 26, 0.04), 0 8px 20px -4px rgba(16, 20, 26, 0.12);
|
|
197
|
+
--shadow-lg: 0 4px 8px -2px rgba(16, 20, 26, 0.06), 0 20px 48px -12px rgba(16, 20, 26, 0.22);
|
|
179
198
|
|
|
180
199
|
/* One shared tier for every FloatingPanel-based dropdown/menu
|
|
181
200
|
(row actions, column picker, combobox, select, table-actions,
|
|
@@ -204,18 +223,30 @@
|
|
|
204
223
|
--space-8: 32px;
|
|
205
224
|
--space-10: 40px;
|
|
206
225
|
--space-12: 48px;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
--
|
|
212
|
-
--
|
|
213
|
-
|
|
226
|
+
/* Two steps past the old ceiling, for the gaps that separate whole
|
|
227
|
+
regions of a page rather than elements inside one. Without them
|
|
228
|
+
every large gap in the app was a hardcoded number, and they were
|
|
229
|
+
all slightly different. */
|
|
230
|
+
--space-16: 64px;
|
|
231
|
+
--space-20: 80px;
|
|
232
|
+
|
|
233
|
+
/* Type scale. The old one grew out of whatever each screen happened
|
|
234
|
+
to be using — 13.5, 14.5, 15 — three steps inside a point and a
|
|
235
|
+
half of each other, which is not a scale, it is three sizes that
|
|
236
|
+
could not agree. This is a proper ~1.2 ratio off a 14px body, so
|
|
237
|
+
the steps are actually distinguishable and a heading reads as a
|
|
238
|
+
heading rather than as slightly larger body text.
|
|
239
|
+
Body moves 14.5 -> 14: at Inter's x-height 14px is the more
|
|
240
|
+
comfortable long-session size, and it buys back the density the
|
|
241
|
+
roomier spacing below spends. */
|
|
242
|
+
--text-xs: 11.5px;
|
|
243
|
+
--text-sm: 13px;
|
|
244
|
+
--text-base: 14px;
|
|
214
245
|
--text-md: 15px;
|
|
215
246
|
--text-lg: 17px;
|
|
216
|
-
--text-xl:
|
|
217
|
-
--text-2xl:
|
|
218
|
-
--text-3xl:
|
|
247
|
+
--text-xl: 20px;
|
|
248
|
+
--text-2xl: 24px;
|
|
249
|
+
--text-3xl: 30px;
|
|
219
250
|
|
|
220
251
|
/* A real weight scale — headings, card titles, and KPI/stat values
|
|
221
252
|
use these to stand apart from the body text around them; dense
|
|
@@ -244,10 +275,10 @@
|
|
|
244
275
|
--hd-card-bg as every card (bordered, not tonally separate),
|
|
245
276
|
mirrored by dark theme's own literal Hattisar Design values below. */
|
|
246
277
|
--sidebar-bg: var(--color-background-card);
|
|
247
|
-
--sidebar-text:
|
|
248
|
-
--sidebar-text-muted:
|
|
278
|
+
--sidebar-text: var(--color-text-primary);
|
|
279
|
+
--sidebar-text-muted: var(--color-text-muted);
|
|
249
280
|
--sidebar-active-bg: var(--color-surface-tint);
|
|
250
|
-
--sidebar-active-text:
|
|
281
|
+
--sidebar-active-text: var(--color-text-primary);
|
|
251
282
|
--sidebar-hover-bg: var(--color-surface-tint);
|
|
252
283
|
/* Icon tint for the active nav row — not a full fill, so
|
|
253
284
|
--sidebar-active-bg/text above stay the "you're here" pill and
|
|
@@ -289,22 +320,22 @@
|
|
|
289
320
|
sits below the hover/tint tier. Modest steps between each (not a
|
|
290
321
|
dramatic jump) plus real borders is what keeps a dark theme
|
|
291
322
|
reading crisp instead of washed out — see --color-border below. */
|
|
292
|
-
--color-background-global: #
|
|
293
|
-
--color-background-card: #
|
|
294
|
-
--color-surface-tint: #
|
|
323
|
+
--color-background-global: #0d0f13;
|
|
324
|
+
--color-background-card: #16191e;
|
|
325
|
+
--color-surface-tint: #1f232a;
|
|
295
326
|
--color-input: var(--color-background-card);
|
|
296
327
|
/* Not pure white — pure white text on a near-black surface causes
|
|
297
328
|
halation (a glow/vibrate effect on the eye over long sessions).
|
|
298
329
|
~92% white instead. */
|
|
299
|
-
--color-text-primary: #
|
|
330
|
+
--color-text-primary: #e9ebef;
|
|
300
331
|
/* ~7:1 on card */
|
|
301
|
-
--color-text-secondary: #
|
|
332
|
+
--color-text-secondary: #9ba4b2;
|
|
302
333
|
/* ~4.7:1 on card — was #5f6b7c at 2.7:1, failing AA body text */
|
|
303
|
-
--color-text-muted: #
|
|
334
|
+
--color-text-muted: #8b94a2;
|
|
304
335
|
/* Derived from text-primary's own RGB (233,233,236) — same
|
|
305
336
|
single-source-of-truth reasoning as :root's own --color-border. */
|
|
306
|
-
--color-border: rgba(233,
|
|
307
|
-
--color-border-strong: rgba(233,
|
|
337
|
+
--color-border: rgba(233, 235, 239, 0.10);
|
|
338
|
+
--color-border-strong: rgba(233, 235, 239, 0.18);
|
|
308
339
|
--color-hover: var(--color-surface-tint);
|
|
309
340
|
|
|
310
341
|
/* Brighter counterparts of :root's accent set — a dark badge fill
|
|
@@ -317,7 +348,7 @@
|
|
|
317
348
|
--accent-violet: #d4ccff;
|
|
318
349
|
--accent-amber: #ffc879;
|
|
319
350
|
--accent-bronze: #ffab73;
|
|
320
|
-
--accent-blue: #
|
|
351
|
+
--accent-blue: #7ba9ff;
|
|
321
352
|
--accent-cyan: #91d0ff;
|
|
322
353
|
--accent-mint: #2ced83;
|
|
323
354
|
--accent-fuchsia: #ff9dbd;
|
|
@@ -329,10 +360,10 @@
|
|
|
329
360
|
--color-brand: var(--color-text-primary);
|
|
330
361
|
--color-brand-hover: color-mix(in srgb, var(--color-brand) 90%, black);
|
|
331
362
|
--color-brand-active: color-mix(in srgb, var(--color-brand) 75%, black);
|
|
332
|
-
--color-brand-text: #
|
|
363
|
+
--color-brand-text: #0d0f13;
|
|
333
364
|
|
|
334
|
-
--shadow-btn-primary:
|
|
335
|
-
--shadow-btn-secondary:
|
|
365
|
+
--shadow-btn-primary: 0 1px 2px rgba(16, 20, 26, 0.12);
|
|
366
|
+
--shadow-btn-secondary: 0 1px 2px rgba(16, 20, 26, 0.05);
|
|
336
367
|
|
|
337
368
|
--color-secondary-fill: var(--color-surface-tint);
|
|
338
369
|
--color-secondary-border: var(--color-border-strong);
|
|
@@ -350,37 +381,41 @@
|
|
|
350
381
|
--color-success: var(--accent-mint);
|
|
351
382
|
|
|
352
383
|
--status-draft-text: var(--accent-slate);
|
|
353
|
-
--status-draft-bg: #
|
|
384
|
+
--status-draft-bg: #262a31;
|
|
354
385
|
--status-backordered-text: var(--accent-violet);
|
|
355
|
-
--status-backordered-bg: #
|
|
386
|
+
--status-backordered-bg: #312747;
|
|
356
387
|
--status-low-stock-text: var(--accent-amber);
|
|
357
|
-
--status-low-stock-bg: #
|
|
388
|
+
--status-low-stock-bg: #3f3411;
|
|
358
389
|
--status-critical-stock-text: var(--color-critical);
|
|
359
|
-
--status-critical-stock-bg: #
|
|
390
|
+
--status-critical-stock-bg: #452c12;
|
|
360
391
|
--status-out-of-stock-text: var(--accent-bronze);
|
|
361
|
-
--status-out-of-stock-bg: #
|
|
392
|
+
--status-out-of-stock-bg: #452916;
|
|
362
393
|
--status-partially-fulfilled-text: var(--accent-blue);
|
|
363
|
-
--status-partially-fulfilled-bg: #
|
|
394
|
+
--status-partially-fulfilled-bg: #1b2740;
|
|
364
395
|
--status-shipped-text: var(--accent-cyan);
|
|
365
|
-
--status-shipped-bg: #
|
|
396
|
+
--status-shipped-bg: #0f3341;
|
|
366
397
|
--status-delivered-text: var(--accent-mint);
|
|
367
|
-
--status-delivered-bg: #
|
|
398
|
+
--status-delivered-bg: #133620;
|
|
368
399
|
--status-cancelled-text: var(--accent-fuchsia);
|
|
369
|
-
--status-cancelled-bg: #
|
|
400
|
+
--status-cancelled-bg: #421f2f;
|
|
370
401
|
--status-overdue-text: var(--color-danger);
|
|
371
|
-
--status-overdue-bg: #
|
|
402
|
+
--status-overdue-bg: #401d21;
|
|
372
403
|
|
|
373
|
-
--banner-warning-bg: #
|
|
404
|
+
--banner-warning-bg: #3a2907;
|
|
374
405
|
--banner-warning-border: var(--color-warning);
|
|
375
|
-
--banner-danger-bg: #
|
|
406
|
+
--banner-danger-bg: #4a1119;
|
|
376
407
|
--banner-danger-border: var(--color-danger);
|
|
377
|
-
--banner-success-bg: #
|
|
408
|
+
--banner-success-bg: #0a3129;
|
|
378
409
|
--banner-success-border: var(--color-success);
|
|
379
410
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
411
|
+
/* A dark surface cannot be lifted by a soft grey shadow — there is
|
|
412
|
+
nothing lighter behind it to catch the light. So these go darker
|
|
413
|
+
and deeper than their light-theme counterparts, which is what
|
|
414
|
+
actually reads as elevation on a near-black page. */
|
|
415
|
+
--shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.32);
|
|
416
|
+
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4), 0 2px 8px -3px rgba(0, 0, 0, 0.5);
|
|
417
|
+
--shadow-md: 0 2px 4px -1px rgba(0, 0, 0, 0.4), 0 8px 22px -4px rgba(0, 0, 0, 0.6);
|
|
418
|
+
--shadow-lg: 0 4px 8px -2px rgba(0, 0, 0, 0.45), 0 24px 56px -12px rgba(0, 0, 0, 0.75);
|
|
384
419
|
|
|
385
420
|
/* Deliberately NOT --color-background-card — the sidebar is now a
|
|
386
421
|
distinct chrome rail, true black, sitting a full step below the
|
|
@@ -409,15 +444,15 @@
|
|
|
409
444
|
takes the nearest declaration in the tree, so relying on :root
|
|
410
445
|
alone would lose to a dark attribute set higher up. */
|
|
411
446
|
[data-theme="light"] {
|
|
412
|
-
--color-background-global: #
|
|
447
|
+
--color-background-global: #f7f8fa;
|
|
413
448
|
--color-background-card: #ffffff;
|
|
414
|
-
--color-surface-tint: #
|
|
415
|
-
--color-input: #
|
|
416
|
-
--color-text-primary: #
|
|
417
|
-
--color-text-secondary: #
|
|
418
|
-
--color-text-muted: #
|
|
419
|
-
--color-border: rgba(
|
|
420
|
-
--color-border-strong: rgba(
|
|
449
|
+
--color-surface-tint: #f0f2f6;
|
|
450
|
+
--color-input: #ffffff;
|
|
451
|
+
--color-text-primary: #10141a;
|
|
452
|
+
--color-text-secondary: #4b5563;
|
|
453
|
+
--color-text-muted: #5f6874;
|
|
454
|
+
--color-border: rgba(16, 20, 26, 0.09);
|
|
455
|
+
--color-border-strong: rgba(16, 20, 26, 0.16);
|
|
421
456
|
--color-hover: var(--color-surface-tint);
|
|
422
457
|
|
|
423
458
|
--color-brand: var(--color-text-primary);
|
|
@@ -425,8 +460,8 @@
|
|
|
425
460
|
--color-brand-active: color-mix(in srgb, var(--color-brand) 70%, black);
|
|
426
461
|
--color-brand-text: #ffffff;
|
|
427
462
|
|
|
428
|
-
--shadow-btn-primary:
|
|
429
|
-
--shadow-btn-secondary:
|
|
463
|
+
--shadow-btn-primary: 0 1px 2px rgba(0, 0, 0, 0.4);
|
|
464
|
+
--shadow-btn-secondary: 0 1px 2px rgba(0, 0, 0, 0.3);
|
|
430
465
|
|
|
431
466
|
--color-secondary-fill: var(--color-surface-tint);
|
|
432
467
|
--color-secondary-border: var(--color-border-strong);
|
|
@@ -443,43 +478,49 @@
|
|
|
443
478
|
--color-success: #047b5d;
|
|
444
479
|
|
|
445
480
|
--status-draft-text: var(--accent-slate);
|
|
446
|
-
--status-draft-bg: #
|
|
481
|
+
--status-draft-bg: #eef1f6;
|
|
447
482
|
--status-backordered-text: var(--accent-violet);
|
|
448
|
-
--status-backordered-bg: #
|
|
483
|
+
--status-backordered-bg: #f3ecfd;
|
|
449
484
|
--status-low-stock-text: var(--accent-amber);
|
|
450
|
-
--status-low-stock-bg: #
|
|
485
|
+
--status-low-stock-bg: #fff1d9;
|
|
451
486
|
--status-critical-stock-text: var(--color-critical);
|
|
452
|
-
--status-critical-stock-bg: #
|
|
487
|
+
--status-critical-stock-bg: #ffe7d1;
|
|
453
488
|
--status-out-of-stock-text: var(--accent-bronze);
|
|
454
|
-
--status-out-of-stock-bg: #
|
|
489
|
+
--status-out-of-stock-bg: #ffeee4;
|
|
455
490
|
--status-partially-fulfilled-text: var(--accent-blue);
|
|
456
|
-
--status-partially-fulfilled-bg: #
|
|
491
|
+
--status-partially-fulfilled-bg: #e8f1fe;
|
|
457
492
|
--status-shipped-text: var(--accent-cyan);
|
|
458
|
-
--status-shipped-bg: #
|
|
493
|
+
--status-shipped-bg: #ddf0fb;
|
|
459
494
|
--status-delivered-text: var(--accent-mint);
|
|
460
|
-
--status-delivered-bg: #
|
|
495
|
+
--status-delivered-bg: #e1f5e6;
|
|
461
496
|
--status-cancelled-text: var(--accent-fuchsia);
|
|
462
|
-
--status-cancelled-bg: #
|
|
497
|
+
--status-cancelled-bg: #fde7ee;
|
|
463
498
|
--status-overdue-text: var(--color-danger);
|
|
464
|
-
--status-overdue-bg: #
|
|
499
|
+
--status-overdue-bg: #feebec;
|
|
465
500
|
|
|
466
|
-
--banner-warning-bg: #
|
|
501
|
+
--banner-warning-bg: #fff3e6;
|
|
467
502
|
--banner-warning-border: var(--color-warning);
|
|
468
|
-
--banner-danger-bg: #
|
|
503
|
+
--banner-danger-bg: #fdeaed;
|
|
469
504
|
--banner-danger-border: var(--color-danger);
|
|
470
|
-
--banner-success-bg: #
|
|
505
|
+
--banner-success-bg: #e2f7e6;
|
|
471
506
|
--banner-success-border: #047b5d;
|
|
472
507
|
|
|
508
|
+
/* Deliberately still flat, unlike :root's. The only thing that pins
|
|
509
|
+
itself to light is a print page, and a shadow on paper is a grey
|
|
510
|
+
smear. This block exists to restate the light palette inside a dark
|
|
511
|
+
ancestor — it is a copy of :root's values by necessity (custom
|
|
512
|
+
properties cannot be re-pointed at another block), so anything
|
|
513
|
+
changed above has to be changed here too. */
|
|
473
514
|
--shadow-xs: none;
|
|
474
515
|
--shadow-sm: none;
|
|
475
516
|
--shadow-md: none;
|
|
476
517
|
--shadow-lg: none;
|
|
477
518
|
|
|
478
519
|
--sidebar-bg: var(--color-background-card);
|
|
479
|
-
--sidebar-text: #
|
|
480
|
-
--sidebar-text-muted:
|
|
520
|
+
--sidebar-text: #10141a;
|
|
521
|
+
--sidebar-text-muted: var(--color-text-muted);
|
|
481
522
|
--sidebar-active-bg: var(--color-surface-tint);
|
|
482
|
-
--sidebar-active-text:
|
|
523
|
+
--sidebar-active-text: var(--color-text-primary);
|
|
483
524
|
--sidebar-hover-bg: var(--color-surface-tint);
|
|
484
525
|
--sidebar-active-icon: var(--sidebar-active-text);
|
|
485
526
|
--sidebar-rail-bg: var(--color-background-card);
|
|
@@ -600,9 +641,24 @@ td a:hover {
|
|
|
600
641
|
font-weight: var(--font-weight-semibold);
|
|
601
642
|
}
|
|
602
643
|
|
|
644
|
+
/* A label should recede so the value it names is what gets scanned.
|
|
645
|
+
Medium weight at a step down from body, in secondary ink — quieter
|
|
646
|
+
than the data, still comfortably above AA. */
|
|
603
647
|
label {
|
|
604
648
|
font-size: var(--text-sm);
|
|
605
|
-
font-weight:
|
|
649
|
+
font-weight: var(--font-weight-medium);
|
|
650
|
+
color: var(--color-text-secondary);
|
|
651
|
+
line-height: 1.4;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/* A required field says so once, here, rather than each form inventing
|
|
655
|
+
its own asterisk-or-not. Marked on the label so it reads before the
|
|
656
|
+
field rather than after it. */
|
|
657
|
+
label[data-required]::after {
|
|
658
|
+
content: "*";
|
|
659
|
+
margin-left: 3px;
|
|
660
|
+
color: var(--color-danger);
|
|
661
|
+
font-weight: var(--font-weight-regular);
|
|
606
662
|
}
|
|
607
663
|
|
|
608
664
|
/* Primary action — highest-contrast neutral fill (--color-brand),
|
|
@@ -717,14 +773,24 @@ button:focus-visible {
|
|
|
717
773
|
background: var(--color-destructive-hover);
|
|
718
774
|
}
|
|
719
775
|
|
|
776
|
+
/* font-family is inherited; size, weight and colour are stated.
|
|
777
|
+
`font: inherit` alone meant a field took its type from whatever
|
|
778
|
+
wrapped it — and most fields in this app are wrapped in their own
|
|
779
|
+
<label>, which is a step smaller and now medium weight. So a field
|
|
780
|
+
inside a label rendered at 13px and one outside at 14px, and the
|
|
781
|
+
label change would have made the wrapped ones semibold. The value a
|
|
782
|
+
person typed should look the same everywhere it appears. */
|
|
720
783
|
input,
|
|
721
784
|
select,
|
|
722
785
|
textarea {
|
|
723
|
-
font: inherit;
|
|
786
|
+
font-family: inherit;
|
|
787
|
+
font-size: var(--text-base);
|
|
788
|
+
font-weight: var(--font-weight-regular);
|
|
789
|
+
line-height: 1.4;
|
|
724
790
|
width: 100%;
|
|
725
|
-
padding:
|
|
726
|
-
margin-top:
|
|
727
|
-
border: 1px solid var(--color-border);
|
|
791
|
+
padding: 8px 12px;
|
|
792
|
+
margin-top: 6px;
|
|
793
|
+
border: 1px solid var(--color-border-strong);
|
|
728
794
|
border-radius: var(--radius-sm);
|
|
729
795
|
background: var(--color-input);
|
|
730
796
|
color: var(--color-text-primary);
|
|
@@ -733,6 +799,11 @@ textarea {
|
|
|
733
799
|
box-shadow 0.15s ease;
|
|
734
800
|
}
|
|
735
801
|
|
|
802
|
+
input::placeholder,
|
|
803
|
+
textarea::placeholder {
|
|
804
|
+
color: var(--color-text-muted);
|
|
805
|
+
}
|
|
806
|
+
|
|
736
807
|
/* The browser's native select arrow renders at its own baseline/size
|
|
737
808
|
per-OS (a mismatched-color chevron and off-center text) —
|
|
738
809
|
appearance:none strips it entirely and this draws a fixed, always-
|
|
@@ -751,10 +822,23 @@ select {
|
|
|
751
822
|
background-position: right 10px center;
|
|
752
823
|
}
|
|
753
824
|
|
|
754
|
-
/* Focus
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
825
|
+
/* Focus: the border goes to brand ink and a soft ring is drawn *by the
|
|
826
|
+
field itself*, with box-shadow rather than outline.
|
|
827
|
+
The border alone was the previous answer, after an `outline` with an
|
|
828
|
+
offset had read as a second rectangle floating around the input. A
|
|
829
|
+
box-shadow spread follows the border-radius exactly, so it reads as
|
|
830
|
+
one thickened edge — which fixes the original complaint without
|
|
831
|
+
leaving keyboard users hunting for which field they are in. */
|
|
832
|
+
input:focus-visible,
|
|
833
|
+
select:focus-visible,
|
|
834
|
+
textarea:focus-visible {
|
|
835
|
+
outline: none;
|
|
836
|
+
border-color: var(--color-brand);
|
|
837
|
+
box-shadow: 0 0 0 3px var(--color-focus-ring);
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
/* Kept for pointer focus too — a click into a field should still show
|
|
841
|
+
which one is live, just without the ring shouting. */
|
|
758
842
|
input:focus,
|
|
759
843
|
select:focus,
|
|
760
844
|
textarea:focus {
|
|
@@ -762,6 +846,31 @@ textarea:focus {
|
|
|
762
846
|
border-color: var(--color-brand);
|
|
763
847
|
}
|
|
764
848
|
|
|
849
|
+
/* A field the browser or the form has judged invalid.
|
|
850
|
+
There was no error state at all: a rejected field looked exactly
|
|
851
|
+
like an accepted one, and the only signal was whatever the server
|
|
852
|
+
said afterwards. :user-invalid rather than :invalid deliberately —
|
|
853
|
+
it waits until somebody has actually interacted, so an untouched
|
|
854
|
+
form does not open covered in red. */
|
|
855
|
+
input[aria-invalid="true"],
|
|
856
|
+
select[aria-invalid="true"],
|
|
857
|
+
textarea[aria-invalid="true"],
|
|
858
|
+
input:user-invalid,
|
|
859
|
+
select:user-invalid,
|
|
860
|
+
textarea:user-invalid {
|
|
861
|
+
border-color: var(--color-danger);
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
input[aria-invalid="true"]:focus-visible,
|
|
865
|
+
select[aria-invalid="true"]:focus-visible,
|
|
866
|
+
textarea[aria-invalid="true"]:focus-visible,
|
|
867
|
+
input:user-invalid:focus-visible,
|
|
868
|
+
select:user-invalid:focus-visible,
|
|
869
|
+
textarea:user-invalid:focus-visible {
|
|
870
|
+
border-color: var(--color-danger);
|
|
871
|
+
box-shadow: 0 0 0 3px var(--color-focus-ring-danger);
|
|
872
|
+
}
|
|
873
|
+
|
|
765
874
|
input:disabled,
|
|
766
875
|
select:disabled,
|
|
767
876
|
textarea:disabled {
|