@hanzo/design 0.5.0 → 0.5.1
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/LICENSE +10 -24
- package/README.md +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/preference.d.ts +65 -0
- package/dist/preference.d.ts.map +1 -0
- package/dist/preference.js +102 -0
- package/dist/tokens.gen.d.ts +74 -70
- package/dist/tokens.gen.d.ts.map +1 -1
- package/dist/tokens.gen.js +74 -70
- package/package.json +8 -6
- package/scripts/check-dist.mjs +81 -0
- package/scripts/check-preference.mjs +168 -0
- package/scripts/check-tokens.mjs +34 -0
- package/skills/design-system/SKILL.md +1 -1
- package/src/index.ts +6 -0
- package/src/preference.ts +122 -0
- package/src/tokens.gen.ts +74 -70
- package/styles.css +82 -50
- package/tailwind.css +82 -50
- package/tokens/base.css +33 -13
- package/tokens/colors.css +9 -9
- package/tokens/spacing.css +20 -14
- package/tokens/typography.css +20 -14
package/styles.css
CHANGED
|
@@ -229,23 +229,23 @@
|
|
|
229
229
|
silent (the border simply stops existing) and has shipped before. */
|
|
230
230
|
.light{
|
|
231
231
|
color-scheme:light;
|
|
232
|
-
--background:#
|
|
232
|
+
--background:#f7f7f7;
|
|
233
233
|
--foreground:#0a0a0a;
|
|
234
234
|
/* A ladder, not four names for #f5f5f5. Light lifts by the same tiny steps
|
|
235
235
|
dark does — ~2% per rung — so --surface-0..3 mean something in both themes. */
|
|
236
|
-
--card:#
|
|
236
|
+
--card:#f2f2f2;
|
|
237
237
|
--card-foreground:#0a0a0a;
|
|
238
|
-
--popover:#
|
|
238
|
+
--popover:#fbfbfb;
|
|
239
239
|
--popover-foreground:#0a0a0a;
|
|
240
240
|
--primary:#0a0a0a;
|
|
241
241
|
--primary-hover:#262626;
|
|
242
242
|
--primary-foreground:#fafafa;
|
|
243
|
-
--secondary:#
|
|
243
|
+
--secondary:#e4e4e4;
|
|
244
244
|
--secondary-hover:#e0e0e0;
|
|
245
245
|
--secondary-foreground:#0a0a0a;
|
|
246
|
-
--muted:#
|
|
246
|
+
--muted:#ededed;
|
|
247
247
|
--muted-foreground:#525252;
|
|
248
|
-
--accent:#
|
|
248
|
+
--accent:#e4e4e4;
|
|
249
249
|
--accent-foreground:#0a0a0a;
|
|
250
250
|
--destructive:var(--state-error);
|
|
251
251
|
--destructive-hover:#dc2626;
|
|
@@ -270,9 +270,9 @@
|
|
|
270
270
|
--selection:rgb(0 0 0 / .16);
|
|
271
271
|
--glass:rgb(0 0 0 / .04);
|
|
272
272
|
--glass-strong:rgb(0 0 0 / .07);
|
|
273
|
-
--surface-card:#
|
|
274
|
-
--surface-card-emphasis:#
|
|
275
|
-
--surface-card-quiet:#
|
|
273
|
+
--surface-card:#f2f2f2;
|
|
274
|
+
--surface-card-emphasis:#fdfdfd;
|
|
275
|
+
--surface-card-quiet:#f5f5f5;
|
|
276
276
|
--surface-overlay:rgb(255 255 255 / .95);
|
|
277
277
|
--surface-header:rgb(255 255 255 / .8);
|
|
278
278
|
--surface-scrim:rgb(0 0 0 / .5);
|
|
@@ -290,19 +290,25 @@
|
|
|
290
290
|
— the two are the SAME scale, mirrored. A surface/tenant overrides any --text-*
|
|
291
291
|
on :root to retune density on demand. */
|
|
292
292
|
:root{
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
--
|
|
298
|
-
|
|
299
|
-
--text-
|
|
300
|
-
--text-
|
|
301
|
-
--text-
|
|
302
|
-
--text-
|
|
303
|
-
--text-
|
|
304
|
-
--text-
|
|
305
|
-
--text-
|
|
293
|
+
/* The type knob. 1 is the published scale; every rung below multiplies by
|
|
294
|
+
it, so a surface or a person retunes the WHOLE ramp by setting one
|
|
295
|
+
number and no rung can drift out of relation with the others.
|
|
296
|
+
@hanzo/design's `vars({type})` emits exactly this name. */
|
|
297
|
+
--type-scale:1;
|
|
298
|
+
|
|
299
|
+
--text-xs:calc(0.6875rem * var(--type-scale, 1)); --leading-xs:calc(1rem * var(--type-scale, 1)); /* 11px — eyebrows / section labels */
|
|
300
|
+
--text-sm:calc(0.8125rem * var(--type-scale, 1)); --leading-sm:calc(1.15rem * var(--type-scale, 1)); /* 13px — nav labels, dense body */
|
|
301
|
+
--text-base:calc(0.875rem * var(--type-scale, 1)); --leading-base:calc(1.35rem * var(--type-scale, 1)); /* 14px — base app text (was 16px) */
|
|
302
|
+
--text-lg:calc(0.9375rem * var(--type-scale, 1)); --leading-lg:calc(1.4rem * var(--type-scale, 1)); /* 15px */
|
|
303
|
+
--text-xl:calc(1.0625rem * var(--type-scale, 1)); --leading-xl:calc(1.55rem * var(--type-scale, 1)); /* 17px */
|
|
304
|
+
--text-2xl:calc(1.3125rem * var(--type-scale, 1)); --leading-2xl:calc(1.7rem * var(--type-scale, 1)); /* 21px */
|
|
305
|
+
--text-3xl:calc(1.625rem * var(--type-scale, 1)); --leading-3xl:calc(1.95rem * var(--type-scale, 1)); /* 26px */
|
|
306
|
+
--text-4xl:calc(2rem * var(--type-scale, 1)); --leading-4xl:calc(2.25rem * var(--type-scale, 1)); /* 32px */
|
|
307
|
+
--text-5xl:calc(2.5rem * var(--type-scale, 1)); --leading-5xl:1.05; /* 40px */
|
|
308
|
+
--text-6xl:calc(3.25rem * var(--type-scale, 1)); --leading-6xl:1; /* 52px */
|
|
309
|
+
--text-7xl:calc(4rem * var(--type-scale, 1)); --leading-7xl:1; /* 64px */
|
|
310
|
+
--text-8xl:calc(5.25rem * var(--type-scale, 1)); --leading-8xl:1; /* 84px */
|
|
311
|
+
--text-9xl:calc(7rem * var(--type-scale, 1)); --leading-9xl:1; /* 112px */
|
|
306
312
|
|
|
307
313
|
/* The SAME scale under @hanzo/brand's spelling. @hanzo/gui's shell theme and
|
|
308
314
|
Hanzo Studio address the ramp as --font-size-*; both names are one value, so
|
|
@@ -367,7 +373,7 @@
|
|
|
367
373
|
form feels broken. pointer:coarse is the real signal — a desktop mouse keeps
|
|
368
374
|
the compact 13px field. */
|
|
369
375
|
@media (pointer:coarse){
|
|
370
|
-
:root{--text-control:1rem}
|
|
376
|
+
:root{--text-control:calc(1rem * var(--type-scale, 1))}
|
|
371
377
|
}
|
|
372
378
|
|
|
373
379
|
/* ── tokens/spacing.css ─────────────────────────────────────── */
|
|
@@ -375,21 +381,27 @@
|
|
|
375
381
|
declared in hanzo.ai's tailwind.config.ts (legacy v3 config, kept for
|
|
376
382
|
reference) — use it for editorial layouts, not for component padding. */
|
|
377
383
|
:root{
|
|
384
|
+
/* The density knob. 1 is the published spacing; every --space-* rung below
|
|
385
|
+
multiplies by it, and --grid-gap-* reference those rungs rather than
|
|
386
|
+
restating them, so gaps, padding and section rhythm move together.
|
|
387
|
+
@hanzo/design's `vars({density})` emits exactly this name. */
|
|
388
|
+
--density:1;
|
|
389
|
+
|
|
378
390
|
--space-0:0;
|
|
379
|
-
--space-1:0.25rem;
|
|
380
|
-
--space-2:0.5rem;
|
|
381
|
-
--space-3:0.75rem;
|
|
382
|
-
--space-4:1rem;
|
|
383
|
-
--space-5:1.25rem;
|
|
384
|
-
--space-6:1.5rem;
|
|
385
|
-
--space-8:2rem;
|
|
386
|
-
--space-10:2.5rem;
|
|
387
|
-
--space-12:3rem;
|
|
388
|
-
--space-14:3.5rem;
|
|
389
|
-
--space-16:4rem;
|
|
390
|
-
--space-20:5rem;
|
|
391
|
-
--space-24:6rem;
|
|
392
|
-
--space-32:8rem;
|
|
391
|
+
--space-1:calc(0.25rem * var(--density, 1));
|
|
392
|
+
--space-2:calc(0.5rem * var(--density, 1));
|
|
393
|
+
--space-3:calc(0.75rem * var(--density, 1));
|
|
394
|
+
--space-4:calc(1rem * var(--density, 1));
|
|
395
|
+
--space-5:calc(1.25rem * var(--density, 1));
|
|
396
|
+
--space-6:calc(1.5rem * var(--density, 1));
|
|
397
|
+
--space-8:calc(2rem * var(--density, 1));
|
|
398
|
+
--space-10:calc(2.5rem * var(--density, 1));
|
|
399
|
+
--space-12:calc(3rem * var(--density, 1));
|
|
400
|
+
--space-14:calc(3.5rem * var(--density, 1));
|
|
401
|
+
--space-16:calc(4rem * var(--density, 1));
|
|
402
|
+
--space-20:calc(5rem * var(--density, 1));
|
|
403
|
+
--space-24:calc(6rem * var(--density, 1));
|
|
404
|
+
--space-32:calc(8rem * var(--density, 1));
|
|
393
405
|
|
|
394
406
|
/* golden ramp (φ) — hanzo.ai tailwind.config.ts */
|
|
395
407
|
--golden-1:0.25rem;
|
|
@@ -834,19 +846,26 @@
|
|
|
834
846
|
padding:0 var(--space-3);
|
|
835
847
|
}
|
|
836
848
|
:where(textarea){padding:var(--space-2) var(--space-3);resize:vertical}
|
|
837
|
-
/*
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
849
|
+
/* A field has NO focus rule of its own — see the ring at the bottom of this
|
|
850
|
+
layer, which is the one focus indicator for everything.
|
|
851
|
+
|
|
852
|
+
There used to be one here: `outline:none` plus a brightened edge (.15 ->
|
|
853
|
+
.22) plus a soft halo, the composer look. It was removed because it could
|
|
854
|
+
not do the job in either of the two ways that matter.
|
|
855
|
+
|
|
856
|
+
It was never VISIBLE ENOUGH. Composited on --background the brightened edge
|
|
857
|
+
measures 1.91:1 and the halo 1.25:1, against the 3:1 that WCAG 1.4.11 asks
|
|
858
|
+
of a focus indicator and that this package already gates --ring on. The
|
|
859
|
+
budget was documented as being spent entirely on --ring "because that is
|
|
860
|
+
what a keyboard user navigates by" — true for a button, and false for a
|
|
861
|
+
field for exactly as long as this rule told fields not to use it.
|
|
862
|
+
|
|
863
|
+
And it was SUPPRESSIBLE. It carried the indicator on `border-color`, so any
|
|
864
|
+
app that states `border` on its own fields overrode it — @hanzo/id does,
|
|
865
|
+
unlayered, which beats this layer whatever its specificity, and its focused
|
|
866
|
+
fields sat at the resting .15 while both files read as correct. An outline
|
|
867
|
+
is not a border: nothing in an app's field styling reaches it, so the ring
|
|
868
|
+
paints whether or not the app has opinions about edges. */
|
|
850
869
|
:where(input,select,textarea):hover:not(:focus-visible):not(:disabled){background:var(--surface-3)}
|
|
851
870
|
:where(input,textarea)::placeholder{color:var(--text-disabled)}
|
|
852
871
|
:where(input,select,textarea,button):disabled{opacity:.5;cursor:not-allowed}
|
|
@@ -886,6 +905,19 @@
|
|
|
886
905
|
}
|
|
887
906
|
}
|
|
888
907
|
|
|
908
|
+
/* THE focus indicator. One rule, every focusable thing, no exceptions — a
|
|
909
|
+
button, a link, a summary, a field. 2px at --ring is 3.77:1 on the darkest
|
|
910
|
+
canvas and clears the 2px perimeter WCAG 2.4.13 asks for; the gate in
|
|
911
|
+
check-tokens holds --ring to that and nothing else here may weaken it.
|
|
912
|
+
|
|
913
|
+
There were two rules until 0.4.9, and they collided invisibly. Both computed
|
|
914
|
+
to (0,1,0) — :where() zeroes whatever it wraps, leaving one pseudo-class on
|
|
915
|
+
each side — so the cascade fell through to SOURCE ORDER inside this layer,
|
|
916
|
+
this rule was written later, and it overrode the `outline:none` the field
|
|
917
|
+
rule stated expressly to prevent it. Every focused input on every consumer
|
|
918
|
+
drew BOTH the ring and the edge+halo. Each rule read as correct alone, which
|
|
919
|
+
is why it survived review in both files; the defect existed only in their
|
|
920
|
+
order. One rule cannot disagree with itself. */
|
|
889
921
|
:focus-visible{outline:2px solid var(--ring);outline-offset:2px}
|
|
890
922
|
/* --white-20 is white-on-white in the light theme, so selection reads through
|
|
891
923
|
--selection, which BOTH themes define. */
|
package/tailwind.css
CHANGED
|
@@ -252,23 +252,23 @@
|
|
|
252
252
|
silent (the border simply stops existing) and has shipped before. */
|
|
253
253
|
.light{
|
|
254
254
|
color-scheme:light;
|
|
255
|
-
--background:#
|
|
255
|
+
--background:#f7f7f7;
|
|
256
256
|
--foreground:#0a0a0a;
|
|
257
257
|
/* A ladder, not four names for #f5f5f5. Light lifts by the same tiny steps
|
|
258
258
|
dark does — ~2% per rung — so --surface-0..3 mean something in both themes. */
|
|
259
|
-
--card:#
|
|
259
|
+
--card:#f2f2f2;
|
|
260
260
|
--card-foreground:#0a0a0a;
|
|
261
|
-
--popover:#
|
|
261
|
+
--popover:#fbfbfb;
|
|
262
262
|
--popover-foreground:#0a0a0a;
|
|
263
263
|
--primary:#0a0a0a;
|
|
264
264
|
--primary-hover:#262626;
|
|
265
265
|
--primary-foreground:#fafafa;
|
|
266
|
-
--secondary:#
|
|
266
|
+
--secondary:#e4e4e4;
|
|
267
267
|
--secondary-hover:#e0e0e0;
|
|
268
268
|
--secondary-foreground:#0a0a0a;
|
|
269
|
-
--muted:#
|
|
269
|
+
--muted:#ededed;
|
|
270
270
|
--muted-foreground:#525252;
|
|
271
|
-
--accent:#
|
|
271
|
+
--accent:#e4e4e4;
|
|
272
272
|
--accent-foreground:#0a0a0a;
|
|
273
273
|
--destructive:var(--state-error);
|
|
274
274
|
--destructive-hover:#dc2626;
|
|
@@ -293,9 +293,9 @@
|
|
|
293
293
|
--selection:rgb(0 0 0 / .16);
|
|
294
294
|
--glass:rgb(0 0 0 / .04);
|
|
295
295
|
--glass-strong:rgb(0 0 0 / .07);
|
|
296
|
-
--surface-card:#
|
|
297
|
-
--surface-card-emphasis:#
|
|
298
|
-
--surface-card-quiet:#
|
|
296
|
+
--surface-card:#f2f2f2;
|
|
297
|
+
--surface-card-emphasis:#fdfdfd;
|
|
298
|
+
--surface-card-quiet:#f5f5f5;
|
|
299
299
|
--surface-overlay:rgb(255 255 255 / .95);
|
|
300
300
|
--surface-header:rgb(255 255 255 / .8);
|
|
301
301
|
--surface-scrim:rgb(0 0 0 / .5);
|
|
@@ -313,19 +313,25 @@
|
|
|
313
313
|
— the two are the SAME scale, mirrored. A surface/tenant overrides any --text-*
|
|
314
314
|
on :root to retune density on demand. */
|
|
315
315
|
:root{
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
--
|
|
321
|
-
|
|
322
|
-
--text-
|
|
323
|
-
--text-
|
|
324
|
-
--text-
|
|
325
|
-
--text-
|
|
326
|
-
--text-
|
|
327
|
-
--text-
|
|
328
|
-
--text-
|
|
316
|
+
/* The type knob. 1 is the published scale; every rung below multiplies by
|
|
317
|
+
it, so a surface or a person retunes the WHOLE ramp by setting one
|
|
318
|
+
number and no rung can drift out of relation with the others.
|
|
319
|
+
@hanzo/design's `vars({type})` emits exactly this name. */
|
|
320
|
+
--type-scale:1;
|
|
321
|
+
|
|
322
|
+
--text-xs:calc(0.6875rem * var(--type-scale, 1)); --leading-xs:calc(1rem * var(--type-scale, 1)); /* 11px — eyebrows / section labels */
|
|
323
|
+
--text-sm:calc(0.8125rem * var(--type-scale, 1)); --leading-sm:calc(1.15rem * var(--type-scale, 1)); /* 13px — nav labels, dense body */
|
|
324
|
+
--text-base:calc(0.875rem * var(--type-scale, 1)); --leading-base:calc(1.35rem * var(--type-scale, 1)); /* 14px — base app text (was 16px) */
|
|
325
|
+
--text-lg:calc(0.9375rem * var(--type-scale, 1)); --leading-lg:calc(1.4rem * var(--type-scale, 1)); /* 15px */
|
|
326
|
+
--text-xl:calc(1.0625rem * var(--type-scale, 1)); --leading-xl:calc(1.55rem * var(--type-scale, 1)); /* 17px */
|
|
327
|
+
--text-2xl:calc(1.3125rem * var(--type-scale, 1)); --leading-2xl:calc(1.7rem * var(--type-scale, 1)); /* 21px */
|
|
328
|
+
--text-3xl:calc(1.625rem * var(--type-scale, 1)); --leading-3xl:calc(1.95rem * var(--type-scale, 1)); /* 26px */
|
|
329
|
+
--text-4xl:calc(2rem * var(--type-scale, 1)); --leading-4xl:calc(2.25rem * var(--type-scale, 1)); /* 32px */
|
|
330
|
+
--text-5xl:calc(2.5rem * var(--type-scale, 1)); --leading-5xl:1.05; /* 40px */
|
|
331
|
+
--text-6xl:calc(3.25rem * var(--type-scale, 1)); --leading-6xl:1; /* 52px */
|
|
332
|
+
--text-7xl:calc(4rem * var(--type-scale, 1)); --leading-7xl:1; /* 64px */
|
|
333
|
+
--text-8xl:calc(5.25rem * var(--type-scale, 1)); --leading-8xl:1; /* 84px */
|
|
334
|
+
--text-9xl:calc(7rem * var(--type-scale, 1)); --leading-9xl:1; /* 112px */
|
|
329
335
|
|
|
330
336
|
/* The SAME scale under @hanzo/brand's spelling. @hanzo/gui's shell theme and
|
|
331
337
|
Hanzo Studio address the ramp as --font-size-*; both names are one value, so
|
|
@@ -390,7 +396,7 @@
|
|
|
390
396
|
form feels broken. pointer:coarse is the real signal — a desktop mouse keeps
|
|
391
397
|
the compact 13px field. */
|
|
392
398
|
@media (pointer:coarse){
|
|
393
|
-
:root{--text-control:1rem}
|
|
399
|
+
:root{--text-control:calc(1rem * var(--type-scale, 1))}
|
|
394
400
|
}
|
|
395
401
|
|
|
396
402
|
/* ── tokens/spacing.css ─────────────────────────────────────── */
|
|
@@ -398,21 +404,27 @@
|
|
|
398
404
|
declared in hanzo.ai's tailwind.config.ts (legacy v3 config, kept for
|
|
399
405
|
reference) — use it for editorial layouts, not for component padding. */
|
|
400
406
|
:root{
|
|
407
|
+
/* The density knob. 1 is the published spacing; every --space-* rung below
|
|
408
|
+
multiplies by it, and --grid-gap-* reference those rungs rather than
|
|
409
|
+
restating them, so gaps, padding and section rhythm move together.
|
|
410
|
+
@hanzo/design's `vars({density})` emits exactly this name. */
|
|
411
|
+
--density:1;
|
|
412
|
+
|
|
401
413
|
--space-0:0;
|
|
402
|
-
--space-1:0.25rem;
|
|
403
|
-
--space-2:0.5rem;
|
|
404
|
-
--space-3:0.75rem;
|
|
405
|
-
--space-4:1rem;
|
|
406
|
-
--space-5:1.25rem;
|
|
407
|
-
--space-6:1.5rem;
|
|
408
|
-
--space-8:2rem;
|
|
409
|
-
--space-10:2.5rem;
|
|
410
|
-
--space-12:3rem;
|
|
411
|
-
--space-14:3.5rem;
|
|
412
|
-
--space-16:4rem;
|
|
413
|
-
--space-20:5rem;
|
|
414
|
-
--space-24:6rem;
|
|
415
|
-
--space-32:8rem;
|
|
414
|
+
--space-1:calc(0.25rem * var(--density, 1));
|
|
415
|
+
--space-2:calc(0.5rem * var(--density, 1));
|
|
416
|
+
--space-3:calc(0.75rem * var(--density, 1));
|
|
417
|
+
--space-4:calc(1rem * var(--density, 1));
|
|
418
|
+
--space-5:calc(1.25rem * var(--density, 1));
|
|
419
|
+
--space-6:calc(1.5rem * var(--density, 1));
|
|
420
|
+
--space-8:calc(2rem * var(--density, 1));
|
|
421
|
+
--space-10:calc(2.5rem * var(--density, 1));
|
|
422
|
+
--space-12:calc(3rem * var(--density, 1));
|
|
423
|
+
--space-14:calc(3.5rem * var(--density, 1));
|
|
424
|
+
--space-16:calc(4rem * var(--density, 1));
|
|
425
|
+
--space-20:calc(5rem * var(--density, 1));
|
|
426
|
+
--space-24:calc(6rem * var(--density, 1));
|
|
427
|
+
--space-32:calc(8rem * var(--density, 1));
|
|
416
428
|
|
|
417
429
|
/* golden ramp (φ) — hanzo.ai tailwind.config.ts */
|
|
418
430
|
--golden-1:0.25rem;
|
|
@@ -857,19 +869,26 @@
|
|
|
857
869
|
padding:0 var(--space-3);
|
|
858
870
|
}
|
|
859
871
|
:where(textarea){padding:var(--space-2) var(--space-3);resize:vertical}
|
|
860
|
-
/*
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
872
|
+
/* A field has NO focus rule of its own — see the ring at the bottom of this
|
|
873
|
+
layer, which is the one focus indicator for everything.
|
|
874
|
+
|
|
875
|
+
There used to be one here: `outline:none` plus a brightened edge (.15 ->
|
|
876
|
+
.22) plus a soft halo, the composer look. It was removed because it could
|
|
877
|
+
not do the job in either of the two ways that matter.
|
|
878
|
+
|
|
879
|
+
It was never VISIBLE ENOUGH. Composited on --background the brightened edge
|
|
880
|
+
measures 1.91:1 and the halo 1.25:1, against the 3:1 that WCAG 1.4.11 asks
|
|
881
|
+
of a focus indicator and that this package already gates --ring on. The
|
|
882
|
+
budget was documented as being spent entirely on --ring "because that is
|
|
883
|
+
what a keyboard user navigates by" — true for a button, and false for a
|
|
884
|
+
field for exactly as long as this rule told fields not to use it.
|
|
885
|
+
|
|
886
|
+
And it was SUPPRESSIBLE. It carried the indicator on `border-color`, so any
|
|
887
|
+
app that states `border` on its own fields overrode it — @hanzo/id does,
|
|
888
|
+
unlayered, which beats this layer whatever its specificity, and its focused
|
|
889
|
+
fields sat at the resting .15 while both files read as correct. An outline
|
|
890
|
+
is not a border: nothing in an app's field styling reaches it, so the ring
|
|
891
|
+
paints whether or not the app has opinions about edges. */
|
|
873
892
|
:where(input,select,textarea):hover:not(:focus-visible):not(:disabled){background:var(--surface-3)}
|
|
874
893
|
:where(input,textarea)::placeholder{color:var(--text-disabled)}
|
|
875
894
|
:where(input,select,textarea,button):disabled{opacity:.5;cursor:not-allowed}
|
|
@@ -909,6 +928,19 @@
|
|
|
909
928
|
}
|
|
910
929
|
}
|
|
911
930
|
|
|
931
|
+
/* THE focus indicator. One rule, every focusable thing, no exceptions — a
|
|
932
|
+
button, a link, a summary, a field. 2px at --ring is 3.77:1 on the darkest
|
|
933
|
+
canvas and clears the 2px perimeter WCAG 2.4.13 asks for; the gate in
|
|
934
|
+
check-tokens holds --ring to that and nothing else here may weaken it.
|
|
935
|
+
|
|
936
|
+
There were two rules until 0.4.9, and they collided invisibly. Both computed
|
|
937
|
+
to (0,1,0) — :where() zeroes whatever it wraps, leaving one pseudo-class on
|
|
938
|
+
each side — so the cascade fell through to SOURCE ORDER inside this layer,
|
|
939
|
+
this rule was written later, and it overrode the `outline:none` the field
|
|
940
|
+
rule stated expressly to prevent it. Every focused input on every consumer
|
|
941
|
+
drew BOTH the ring and the edge+halo. Each rule read as correct alone, which
|
|
942
|
+
is why it survived review in both files; the defect existed only in their
|
|
943
|
+
order. One rule cannot disagree with itself. */
|
|
912
944
|
:focus-visible{outline:2px solid var(--ring);outline-offset:2px}
|
|
913
945
|
/* --white-20 is white-on-white in the light theme, so selection reads through
|
|
914
946
|
--selection, which BOTH themes define. */
|
package/tokens/base.css
CHANGED
|
@@ -99,19 +99,26 @@
|
|
|
99
99
|
padding:0 var(--space-3);
|
|
100
100
|
}
|
|
101
101
|
:where(textarea){padding:var(--space-2) var(--space-3);resize:vertical}
|
|
102
|
-
/*
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
102
|
+
/* A field has NO focus rule of its own — see the ring at the bottom of this
|
|
103
|
+
layer, which is the one focus indicator for everything.
|
|
104
|
+
|
|
105
|
+
There used to be one here: `outline:none` plus a brightened edge (.15 ->
|
|
106
|
+
.22) plus a soft halo, the composer look. It was removed because it could
|
|
107
|
+
not do the job in either of the two ways that matter.
|
|
108
|
+
|
|
109
|
+
It was never VISIBLE ENOUGH. Composited on --background the brightened edge
|
|
110
|
+
measures 1.91:1 and the halo 1.25:1, against the 3:1 that WCAG 1.4.11 asks
|
|
111
|
+
of a focus indicator and that this package already gates --ring on. The
|
|
112
|
+
budget was documented as being spent entirely on --ring "because that is
|
|
113
|
+
what a keyboard user navigates by" — true for a button, and false for a
|
|
114
|
+
field for exactly as long as this rule told fields not to use it.
|
|
115
|
+
|
|
116
|
+
And it was SUPPRESSIBLE. It carried the indicator on `border-color`, so any
|
|
117
|
+
app that states `border` on its own fields overrode it — @hanzo/id does,
|
|
118
|
+
unlayered, which beats this layer whatever its specificity, and its focused
|
|
119
|
+
fields sat at the resting .15 while both files read as correct. An outline
|
|
120
|
+
is not a border: nothing in an app's field styling reaches it, so the ring
|
|
121
|
+
paints whether or not the app has opinions about edges. */
|
|
115
122
|
:where(input,select,textarea):hover:not(:focus-visible):not(:disabled){background:var(--surface-3)}
|
|
116
123
|
:where(input,textarea)::placeholder{color:var(--text-disabled)}
|
|
117
124
|
:where(input,select,textarea,button):disabled{opacity:.5;cursor:not-allowed}
|
|
@@ -151,6 +158,19 @@
|
|
|
151
158
|
}
|
|
152
159
|
}
|
|
153
160
|
|
|
161
|
+
/* THE focus indicator. One rule, every focusable thing, no exceptions — a
|
|
162
|
+
button, a link, a summary, a field. 2px at --ring is 3.77:1 on the darkest
|
|
163
|
+
canvas and clears the 2px perimeter WCAG 2.4.13 asks for; the gate in
|
|
164
|
+
check-tokens holds --ring to that and nothing else here may weaken it.
|
|
165
|
+
|
|
166
|
+
There were two rules until 0.4.9, and they collided invisibly. Both computed
|
|
167
|
+
to (0,1,0) — :where() zeroes whatever it wraps, leaving one pseudo-class on
|
|
168
|
+
each side — so the cascade fell through to SOURCE ORDER inside this layer,
|
|
169
|
+
this rule was written later, and it overrode the `outline:none` the field
|
|
170
|
+
rule stated expressly to prevent it. Every focused input on every consumer
|
|
171
|
+
drew BOTH the ring and the edge+halo. Each rule read as correct alone, which
|
|
172
|
+
is why it survived review in both files; the defect existed only in their
|
|
173
|
+
order. One rule cannot disagree with itself. */
|
|
154
174
|
:focus-visible{outline:2px solid var(--ring);outline-offset:2px}
|
|
155
175
|
/* --white-20 is white-on-white in the light theme, so selection reads through
|
|
156
176
|
--selection, which BOTH themes define. */
|
package/tokens/colors.css
CHANGED
|
@@ -214,23 +214,23 @@
|
|
|
214
214
|
silent (the border simply stops existing) and has shipped before. */
|
|
215
215
|
.light{
|
|
216
216
|
color-scheme:light;
|
|
217
|
-
--background:#
|
|
217
|
+
--background:#f7f7f7;
|
|
218
218
|
--foreground:#0a0a0a;
|
|
219
219
|
/* A ladder, not four names for #f5f5f5. Light lifts by the same tiny steps
|
|
220
220
|
dark does — ~2% per rung — so --surface-0..3 mean something in both themes. */
|
|
221
|
-
--card:#
|
|
221
|
+
--card:#f2f2f2;
|
|
222
222
|
--card-foreground:#0a0a0a;
|
|
223
|
-
--popover:#
|
|
223
|
+
--popover:#fbfbfb;
|
|
224
224
|
--popover-foreground:#0a0a0a;
|
|
225
225
|
--primary:#0a0a0a;
|
|
226
226
|
--primary-hover:#262626;
|
|
227
227
|
--primary-foreground:#fafafa;
|
|
228
|
-
--secondary:#
|
|
228
|
+
--secondary:#e4e4e4;
|
|
229
229
|
--secondary-hover:#e0e0e0;
|
|
230
230
|
--secondary-foreground:#0a0a0a;
|
|
231
|
-
--muted:#
|
|
231
|
+
--muted:#ededed;
|
|
232
232
|
--muted-foreground:#525252;
|
|
233
|
-
--accent:#
|
|
233
|
+
--accent:#e4e4e4;
|
|
234
234
|
--accent-foreground:#0a0a0a;
|
|
235
235
|
--destructive:var(--state-error);
|
|
236
236
|
--destructive-hover:#dc2626;
|
|
@@ -255,9 +255,9 @@
|
|
|
255
255
|
--selection:rgb(0 0 0 / .16);
|
|
256
256
|
--glass:rgb(0 0 0 / .04);
|
|
257
257
|
--glass-strong:rgb(0 0 0 / .07);
|
|
258
|
-
--surface-card:#
|
|
259
|
-
--surface-card-emphasis:#
|
|
260
|
-
--surface-card-quiet:#
|
|
258
|
+
--surface-card:#f2f2f2;
|
|
259
|
+
--surface-card-emphasis:#fdfdfd;
|
|
260
|
+
--surface-card-quiet:#f5f5f5;
|
|
261
261
|
--surface-overlay:rgb(255 255 255 / .95);
|
|
262
262
|
--surface-header:rgb(255 255 255 / .8);
|
|
263
263
|
--surface-scrim:rgb(0 0 0 / .5);
|
package/tokens/spacing.css
CHANGED
|
@@ -2,21 +2,27 @@
|
|
|
2
2
|
declared in hanzo.ai's tailwind.config.ts (legacy v3 config, kept for
|
|
3
3
|
reference) — use it for editorial layouts, not for component padding. */
|
|
4
4
|
:root{
|
|
5
|
+
/* The density knob. 1 is the published spacing; every --space-* rung below
|
|
6
|
+
multiplies by it, and --grid-gap-* reference those rungs rather than
|
|
7
|
+
restating them, so gaps, padding and section rhythm move together.
|
|
8
|
+
@hanzo/design's `vars({density})` emits exactly this name. */
|
|
9
|
+
--density:1;
|
|
10
|
+
|
|
5
11
|
--space-0:0;
|
|
6
|
-
--space-1:0.25rem;
|
|
7
|
-
--space-2:0.5rem;
|
|
8
|
-
--space-3:0.75rem;
|
|
9
|
-
--space-4:1rem;
|
|
10
|
-
--space-5:1.25rem;
|
|
11
|
-
--space-6:1.5rem;
|
|
12
|
-
--space-8:2rem;
|
|
13
|
-
--space-10:2.5rem;
|
|
14
|
-
--space-12:3rem;
|
|
15
|
-
--space-14:3.5rem;
|
|
16
|
-
--space-16:4rem;
|
|
17
|
-
--space-20:5rem;
|
|
18
|
-
--space-24:6rem;
|
|
19
|
-
--space-32:8rem;
|
|
12
|
+
--space-1:calc(0.25rem * var(--density, 1));
|
|
13
|
+
--space-2:calc(0.5rem * var(--density, 1));
|
|
14
|
+
--space-3:calc(0.75rem * var(--density, 1));
|
|
15
|
+
--space-4:calc(1rem * var(--density, 1));
|
|
16
|
+
--space-5:calc(1.25rem * var(--density, 1));
|
|
17
|
+
--space-6:calc(1.5rem * var(--density, 1));
|
|
18
|
+
--space-8:calc(2rem * var(--density, 1));
|
|
19
|
+
--space-10:calc(2.5rem * var(--density, 1));
|
|
20
|
+
--space-12:calc(3rem * var(--density, 1));
|
|
21
|
+
--space-14:calc(3.5rem * var(--density, 1));
|
|
22
|
+
--space-16:calc(4rem * var(--density, 1));
|
|
23
|
+
--space-20:calc(5rem * var(--density, 1));
|
|
24
|
+
--space-24:calc(6rem * var(--density, 1));
|
|
25
|
+
--space-32:calc(8rem * var(--density, 1));
|
|
20
26
|
|
|
21
27
|
/* golden ramp (φ) — hanzo.ai tailwind.config.ts */
|
|
22
28
|
--golden-1:0.25rem;
|
package/tokens/typography.css
CHANGED
|
@@ -5,19 +5,25 @@
|
|
|
5
5
|
— the two are the SAME scale, mirrored. A surface/tenant overrides any --text-*
|
|
6
6
|
on :root to retune density on demand. */
|
|
7
7
|
:root{
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
--
|
|
13
|
-
|
|
14
|
-
--text-
|
|
15
|
-
--text-
|
|
16
|
-
--text-
|
|
17
|
-
--text-
|
|
18
|
-
--text-
|
|
19
|
-
--text-
|
|
20
|
-
--text-
|
|
8
|
+
/* The type knob. 1 is the published scale; every rung below multiplies by
|
|
9
|
+
it, so a surface or a person retunes the WHOLE ramp by setting one
|
|
10
|
+
number and no rung can drift out of relation with the others.
|
|
11
|
+
@hanzo/design's `vars({type})` emits exactly this name. */
|
|
12
|
+
--type-scale:1;
|
|
13
|
+
|
|
14
|
+
--text-xs:calc(0.6875rem * var(--type-scale, 1)); --leading-xs:calc(1rem * var(--type-scale, 1)); /* 11px — eyebrows / section labels */
|
|
15
|
+
--text-sm:calc(0.8125rem * var(--type-scale, 1)); --leading-sm:calc(1.15rem * var(--type-scale, 1)); /* 13px — nav labels, dense body */
|
|
16
|
+
--text-base:calc(0.875rem * var(--type-scale, 1)); --leading-base:calc(1.35rem * var(--type-scale, 1)); /* 14px — base app text (was 16px) */
|
|
17
|
+
--text-lg:calc(0.9375rem * var(--type-scale, 1)); --leading-lg:calc(1.4rem * var(--type-scale, 1)); /* 15px */
|
|
18
|
+
--text-xl:calc(1.0625rem * var(--type-scale, 1)); --leading-xl:calc(1.55rem * var(--type-scale, 1)); /* 17px */
|
|
19
|
+
--text-2xl:calc(1.3125rem * var(--type-scale, 1)); --leading-2xl:calc(1.7rem * var(--type-scale, 1)); /* 21px */
|
|
20
|
+
--text-3xl:calc(1.625rem * var(--type-scale, 1)); --leading-3xl:calc(1.95rem * var(--type-scale, 1)); /* 26px */
|
|
21
|
+
--text-4xl:calc(2rem * var(--type-scale, 1)); --leading-4xl:calc(2.25rem * var(--type-scale, 1)); /* 32px */
|
|
22
|
+
--text-5xl:calc(2.5rem * var(--type-scale, 1)); --leading-5xl:1.05; /* 40px */
|
|
23
|
+
--text-6xl:calc(3.25rem * var(--type-scale, 1)); --leading-6xl:1; /* 52px */
|
|
24
|
+
--text-7xl:calc(4rem * var(--type-scale, 1)); --leading-7xl:1; /* 64px */
|
|
25
|
+
--text-8xl:calc(5.25rem * var(--type-scale, 1)); --leading-8xl:1; /* 84px */
|
|
26
|
+
--text-9xl:calc(7rem * var(--type-scale, 1)); --leading-9xl:1; /* 112px */
|
|
21
27
|
|
|
22
28
|
/* The SAME scale under @hanzo/brand's spelling. @hanzo/gui's shell theme and
|
|
23
29
|
Hanzo Studio address the ramp as --font-size-*; both names are one value, so
|
|
@@ -82,5 +88,5 @@
|
|
|
82
88
|
form feels broken. pointer:coarse is the real signal — a desktop mouse keeps
|
|
83
89
|
the compact 13px field. */
|
|
84
90
|
@media (pointer:coarse){
|
|
85
|
-
:root{--text-control:1rem}
|
|
91
|
+
:root{--text-control:calc(1rem * var(--type-scale, 1))}
|
|
86
92
|
}
|