@hanzo/design 0.5.0 → 0.5.2

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/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:#ffffff;
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:#fafafa;
259
+ --card:#f2f2f2;
260
260
  --card-foreground:#0a0a0a;
261
- --popover:#ffffff;
261
+ --popover:#fbfbfb;
262
262
  --popover-foreground:#0a0a0a;
263
263
  --primary:#0a0a0a;
264
264
  --primary-hover:#262626;
265
265
  --primary-foreground:#fafafa;
266
- --secondary:#ededed;
266
+ --secondary:#e4e4e4;
267
267
  --secondary-hover:#e0e0e0;
268
268
  --secondary-foreground:#0a0a0a;
269
- --muted:#f5f5f5;
269
+ --muted:#ededed;
270
270
  --muted-foreground:#525252;
271
- --accent:#ededed;
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:#fafafa;
297
- --surface-card-emphasis:#ffffff;
298
- --surface-card-quiet:#fcfcfc;
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
- --text-xs:0.6875rem; --leading-xs:1rem; /* 11px eyebrows / section labels */
317
- --text-sm:0.8125rem; --leading-sm:1.15rem; /* 13px nav labels, dense body */
318
- --text-base:0.875rem; --leading-base:1.35rem; /* 14px base app text (was 16px) */
319
- --text-lg:0.9375rem; --leading-lg:1.4rem; /* 15px */
320
- --text-xl:1.0625rem; --leading-xl:1.55rem; /* 17px */
321
- --text-2xl:1.3125rem; --leading-2xl:1.7rem; /* 21px */
322
- --text-3xl:1.625rem; --leading-3xl:1.95rem; /* 26px */
323
- --text-4xl:2rem; --leading-4xl:2.25rem; /* 32px */
324
- --text-5xl:2.5rem; --leading-5xl:1.05; /* 40px */
325
- --text-6xl:3.25rem; --leading-6xl:1; /* 52px */
326
- --text-7xl:4rem; --leading-7xl:1; /* 64px */
327
- --text-8xl:5.25rem; --leading-8xl:1; /* 84px */
328
- --text-9xl:7rem; --leading-9xl:1; /* 112px */
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;
@@ -599,6 +611,41 @@
599
611
  --shadow-lg:0 10px 15px -3px rgb(0 0 0 / .55), 0 4px 6px -4px rgb(0 0 0 / .55);
600
612
  --shadow-xl:0 20px 25px -5px rgb(0 0 0 / .60), 0 8px 10px -6px rgb(0 0 0 / .60);
601
613
  --shadow-2xl:var(--shadow-floating);
614
+
615
+ /* ——— the paper ——— */
616
+ /* Depth carries STATE, not decoration: which sheet is the workspace, which is
617
+ the tool in use, which one opened. Reach for a level before a border — if
618
+ two surfaces need separating, RAISE one rather than outline both, and a
619
+ surface that is already raised does not also need a box drawn round it.
620
+
621
+ A level is a tint AND the light on it, published as a pair, because the
622
+ alternative — every component composing its own `--edge-highlight,
623
+ --shadow-*` — is how two panels on one screen end up lit from two
624
+ directions. Every rung here is pure vertical: a 1px inset line along the
625
+ top edge, a drop straight down. One origin, above and slightly in front.
626
+
627
+ The tints are the surface recipes rather than new values, so sheets stack
628
+ the way paper does: each is an alpha-white wash, one laid on another lands
629
+ a step lighter, and the stack converges on #262626 instead of blowing out.
630
+
631
+ Nothing here is restated in `.light`, deliberately: every part defers to a
632
+ token that already flips, so the ramp inverts with the theme for free. */
633
+ --sheet-0:var(--surface-page); /* the workspace ground */
634
+ --sheet-1:var(--surface-card); /* an active tool */
635
+ --sheet-2:var(--surface-card-emphasis); /* what opened, or what is inside */
636
+ --shadow-sheet-1:var(--edge-highlight),var(--shadow-md);
637
+ --shadow-sheet-2:var(--edge-highlight),var(--shadow-lg);
638
+
639
+ /* The fold — a corner turned back, and the one mark that means THIS OPENS.
640
+ Ornament nowhere: a sheet that does not expand does not wear one.
641
+ It is a background-image, so it costs no element and no pseudo-element —
642
+ paint it on the sheet itself and square that corner
643
+ (`border-bottom-right-radius:0`), because a folded corner is not round.
644
+ The flap catches the light like every other surface; the crease is the
645
+ hairline; both defer, so both invert. */
646
+ --fold:12px;
647
+ --fold-face:linear-gradient(315deg,var(--glass-strong) 0,var(--glass-strong) calc(var(--fold) - 1px),var(--border-strong) calc(var(--fold) - 1px),var(--border-strong) var(--fold),transparent var(--fold)); /* @kind color */
648
+
602
649
  /* Ambient hero glow — a single white radial, blurred 120px, low opacity. */
603
650
  --glow-hero:radial-gradient(circle,rgb(255 255 255 / .12) 0%,transparent 68%); /* @kind color */
604
651
  --glow-hero-blur:120px;
@@ -857,19 +904,26 @@
857
904
  padding:0 var(--space-3);
858
905
  }
859
906
  :where(textarea){padding:var(--space-2) var(--space-3);resize:vertical}
860
- /* ——— focus, on a control ———
861
- Not the generic ring. A field already HAS an edge, so focus brightens that
862
- edge (.15 -> .22) and adds a soft halo just outside it — which is what the
863
- reference does (.composer-box:focus-within) and what separates a focused
864
- field from a browser default. The generic `outline` is suppressed here
865
- precisely because it would draw a second, harder box around this one. */
866
- :where(input,select,textarea):focus-visible{
867
- outline:none;
868
- border-color:var(--border-focus);
869
- box-shadow:var(--ring-focus);
870
- }
871
- /* A hovered field lifts its surface a rung. Nudging its EDGE from .15 to .16
872
- is a state nobody can see. */
907
+ /* A field has NO focus rule of its own — see the ring at the bottom of this
908
+ layer, which is the one focus indicator for everything.
909
+
910
+ There used to be one here: `outline:none` plus a brightened edge (.15 ->
911
+ .22) plus a soft halo, the composer look. It was removed because it could
912
+ not do the job in either of the two ways that matter.
913
+
914
+ It was never VISIBLE ENOUGH. Composited on --background the brightened edge
915
+ measures 1.91:1 and the halo 1.25:1, against the 3:1 that WCAG 1.4.11 asks
916
+ of a focus indicator and that this package already gates --ring on. The
917
+ budget was documented as being spent entirely on --ring "because that is
918
+ what a keyboard user navigates by" true for a button, and false for a
919
+ field for exactly as long as this rule told fields not to use it.
920
+
921
+ And it was SUPPRESSIBLE. It carried the indicator on `border-color`, so any
922
+ app that states `border` on its own fields overrode it — @hanzo/id does,
923
+ unlayered, which beats this layer whatever its specificity, and its focused
924
+ fields sat at the resting .15 while both files read as correct. An outline
925
+ is not a border: nothing in an app's field styling reaches it, so the ring
926
+ paints whether or not the app has opinions about edges. */
873
927
  :where(input,select,textarea):hover:not(:focus-visible):not(:disabled){background:var(--surface-3)}
874
928
  :where(input,textarea)::placeholder{color:var(--text-disabled)}
875
929
  :where(input,select,textarea,button):disabled{opacity:.5;cursor:not-allowed}
@@ -909,6 +963,19 @@
909
963
  }
910
964
  }
911
965
 
966
+ /* THE focus indicator. One rule, every focusable thing, no exceptions — a
967
+ button, a link, a summary, a field. 2px at --ring is 3.77:1 on the darkest
968
+ canvas and clears the 2px perimeter WCAG 2.4.13 asks for; the gate in
969
+ check-tokens holds --ring to that and nothing else here may weaken it.
970
+
971
+ There were two rules until 0.4.9, and they collided invisibly. Both computed
972
+ to (0,1,0) — :where() zeroes whatever it wraps, leaving one pseudo-class on
973
+ each side — so the cascade fell through to SOURCE ORDER inside this layer,
974
+ this rule was written later, and it overrode the `outline:none` the field
975
+ rule stated expressly to prevent it. Every focused input on every consumer
976
+ drew BOTH the ring and the edge+halo. Each rule read as correct alone, which
977
+ is why it survived review in both files; the defect existed only in their
978
+ order. One rule cannot disagree with itself. */
912
979
  :focus-visible{outline:2px solid var(--ring);outline-offset:2px}
913
980
  /* --white-20 is white-on-white in the light theme, so selection reads through
914
981
  --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
- /* ——— focus, on a control ———
103
- Not the generic ring. A field already HAS an edge, so focus brightens that
104
- edge (.15 -> .22) and adds a soft halo just outside it — which is what the
105
- reference does (.composer-box:focus-within) and what separates a focused
106
- field from a browser default. The generic `outline` is suppressed here
107
- precisely because it would draw a second, harder box around this one. */
108
- :where(input,select,textarea):focus-visible{
109
- outline:none;
110
- border-color:var(--border-focus);
111
- box-shadow:var(--ring-focus);
112
- }
113
- /* A hovered field lifts its surface a rung. Nudging its EDGE from .15 to .16
114
- is a state nobody can see. */
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:#ffffff;
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:#fafafa;
221
+ --card:#f2f2f2;
222
222
  --card-foreground:#0a0a0a;
223
- --popover:#ffffff;
223
+ --popover:#fbfbfb;
224
224
  --popover-foreground:#0a0a0a;
225
225
  --primary:#0a0a0a;
226
226
  --primary-hover:#262626;
227
227
  --primary-foreground:#fafafa;
228
- --secondary:#ededed;
228
+ --secondary:#e4e4e4;
229
229
  --secondary-hover:#e0e0e0;
230
230
  --secondary-foreground:#0a0a0a;
231
- --muted:#f5f5f5;
231
+ --muted:#ededed;
232
232
  --muted-foreground:#525252;
233
- --accent:#ededed;
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:#fafafa;
259
- --surface-card-emphasis:#ffffff;
260
- --surface-card-quiet:#fcfcfc;
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);
@@ -53,6 +53,41 @@
53
53
  --shadow-lg:0 10px 15px -3px rgb(0 0 0 / .55), 0 4px 6px -4px rgb(0 0 0 / .55);
54
54
  --shadow-xl:0 20px 25px -5px rgb(0 0 0 / .60), 0 8px 10px -6px rgb(0 0 0 / .60);
55
55
  --shadow-2xl:var(--shadow-floating);
56
+
57
+ /* ——— the paper ——— */
58
+ /* Depth carries STATE, not decoration: which sheet is the workspace, which is
59
+ the tool in use, which one opened. Reach for a level before a border — if
60
+ two surfaces need separating, RAISE one rather than outline both, and a
61
+ surface that is already raised does not also need a box drawn round it.
62
+
63
+ A level is a tint AND the light on it, published as a pair, because the
64
+ alternative — every component composing its own `--edge-highlight,
65
+ --shadow-*` — is how two panels on one screen end up lit from two
66
+ directions. Every rung here is pure vertical: a 1px inset line along the
67
+ top edge, a drop straight down. One origin, above and slightly in front.
68
+
69
+ The tints are the surface recipes rather than new values, so sheets stack
70
+ the way paper does: each is an alpha-white wash, one laid on another lands
71
+ a step lighter, and the stack converges on #262626 instead of blowing out.
72
+
73
+ Nothing here is restated in `.light`, deliberately: every part defers to a
74
+ token that already flips, so the ramp inverts with the theme for free. */
75
+ --sheet-0:var(--surface-page); /* the workspace ground */
76
+ --sheet-1:var(--surface-card); /* an active tool */
77
+ --sheet-2:var(--surface-card-emphasis); /* what opened, or what is inside */
78
+ --shadow-sheet-1:var(--edge-highlight),var(--shadow-md);
79
+ --shadow-sheet-2:var(--edge-highlight),var(--shadow-lg);
80
+
81
+ /* The fold — a corner turned back, and the one mark that means THIS OPENS.
82
+ Ornament nowhere: a sheet that does not expand does not wear one.
83
+ It is a background-image, so it costs no element and no pseudo-element —
84
+ paint it on the sheet itself and square that corner
85
+ (`border-bottom-right-radius:0`), because a folded corner is not round.
86
+ The flap catches the light like every other surface; the crease is the
87
+ hairline; both defer, so both invert. */
88
+ --fold:12px;
89
+ --fold-face:linear-gradient(315deg,var(--glass-strong) 0,var(--glass-strong) calc(var(--fold) - 1px),var(--border-strong) calc(var(--fold) - 1px),var(--border-strong) var(--fold),transparent var(--fold)); /* @kind color */
90
+
56
91
  /* Ambient hero glow — a single white radial, blurred 120px, low opacity. */
57
92
  --glow-hero:radial-gradient(circle,rgb(255 255 255 / .12) 0%,transparent 68%); /* @kind color */
58
93
  --glow-hero-blur:120px;
@@ -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;
@@ -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
- --text-xs:0.6875rem; --leading-xs:1rem; /* 11px eyebrows / section labels */
9
- --text-sm:0.8125rem; --leading-sm:1.15rem; /* 13px nav labels, dense body */
10
- --text-base:0.875rem; --leading-base:1.35rem; /* 14px base app text (was 16px) */
11
- --text-lg:0.9375rem; --leading-lg:1.4rem; /* 15px */
12
- --text-xl:1.0625rem; --leading-xl:1.55rem; /* 17px */
13
- --text-2xl:1.3125rem; --leading-2xl:1.7rem; /* 21px */
14
- --text-3xl:1.625rem; --leading-3xl:1.95rem; /* 26px */
15
- --text-4xl:2rem; --leading-4xl:2.25rem; /* 32px */
16
- --text-5xl:2.5rem; --leading-5xl:1.05; /* 40px */
17
- --text-6xl:3.25rem; --leading-6xl:1; /* 52px */
18
- --text-7xl:4rem; --leading-7xl:1; /* 64px */
19
- --text-8xl:5.25rem; --leading-8xl:1; /* 84px */
20
- --text-9xl:7rem; --leading-9xl:1; /* 112px */
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
  }