@signal9/era-ui 20.0.0 → 21.0.0

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.
@@ -6,45 +6,43 @@
6
6
  * TIER LADDER
7
7
  * ===========
8
8
  *
9
- * xs = sp + 6 (icon tierthe glyph itself; thumbs, dismiss, icon button)
10
- * xxs = sp + 10 (pill that nests inside md badge, chip in a button/trigger)
11
- * sm = 2·sp + 6 (standalone pill switch track, KV row, free badge/chip)
12
- * md = 3·sp (buttons, inputs, selects, triggers)
13
- * lg = 4·sp (bars, containers)
9
+ * The rungs icon, pill, chip, control, bar are listed with their roles and
10
+ * per-mode values where they are declared, below. What belongs up here is why
11
+ * each formula has the shape it does:
14
12
  *
15
- * xs carries a +6 floor so icons stay legible (≥14px) at every sp. xxs sits
16
- * 4px above xs so body text has just enough pill around it while fitting
17
- * inside an md container with a visible concentric gap (≥3px at dense). sm
18
- * keeps the generous 2·sp + 6 that gives switch thumbs their sp/2 slot and
19
- * KV rows their padded feel. md and lg are pure sp multiples so button and
20
- * bar heights don't inflate.
13
+ * icon (sp + 6) a +6 floor so a glyph stays legible (≥14px) at every sp.
14
+ * pill (sp + 10) 4px above icon: enough box around body text to read as
15
+ * a pill, while still fitting inside a control with a
16
+ * visible concentric gap (≥3px at dense).
17
+ * chip (2·sp + 6) the generous rung gives switch thumbs their sp/2 slot
18
+ * and KV rows their padded feel.
19
+ * control (3·sp) pure sp multiples, so button and bar heights don't
20
+ * bar (4·sp) inflate as the modes loosen.
21
21
  *
22
22
  * Concentric nesting gaps:
23
- * sm xs = sp/2 (4/5/6 at dense/balanced/spacious)
24
- * lg md = sp/2 (4/5/6)
25
- * md sm = (sp−6)/2 (1/2/3 — tight but present)
26
- * md xxs = (2sp−10)/2 (3/5/7 — the "pill inside a button" gap)
27
- * xxs xs = 2 (flat; xxs is sized around text, not as a
28
- * container for xs content)
23
+ * chip icon = sp/2 (4/5/6 at dense/balanced/spacious)
24
+ * bar control = sp/2 (4/5/6)
25
+ * control chip = (sp−6)/2 (1/2/3 — tight but present)
26
+ * control pill = (2sp−10)/2 (3/5/7 — the "pill inside a button" gap)
27
+ * pill icon = 2 (flat; pill is sized around text, not as a
28
+ * container for icon content)
29
29
  *
30
- * Invariant xs < xxs < sm < md < lg holds for sp ≥ 7. Canonical range is
31
- * sp ∈ [8, 16] so this is mode-bounded.
30
+ * Invariant icon < pill < chip < control < bar holds for sp ≥ 7. Canonical
31
+ * range is sp ∈ [8, 16] so this is mode-bounded.
32
32
  *
33
- * xs IS the icon tier — no separate "icon" token. "Container for an icon"
34
- * and "the icon itself" are the same thing; any padding lives on the parent
35
- * (xxs / sm / md / lg).
36
- *
37
- * Dense values: 14, 18, 22, 24, 32.
38
- * Touch values: 22, 26, 38, 48, 64 (md ≥ 44px tap-target floor).
33
+ * icon IS the icon tier — there is no separate token for "a box holding one
34
+ * glyph", because that box and the glyph are the same size; any padding lives
35
+ * on the parent rung.
39
36
  *
40
37
  * INSETS
41
38
  * ======
42
39
  *
43
- * --era-px-{sm,md,lg} sp/2 tier-to-tier nesting padding
44
- * --era-gap sp/2 — inter-element gap
45
- * --era-inset-{xxs,sm,md} (T − text)/2 — text-symmetric content padding
46
- * (text is 12px at dense, 14 above)
47
- * --era-icon-inset-chip (smxs)/2 = sp/2 centering an xs element inside sm
40
+ * --era-gap sp/2 inter-element gap, and the
41
+ * tier-to-tier nesting padding
42
+ * --era-inset-{pill,chip,control} (T − text)/2 — text-symmetric content padding
43
+ * (text is 12px at dense, 14 above)
44
+ * --era-icon-inset-chip (chipicon)/2 — centring an icon-tier element
45
+ * = sp/2 inside a chip
48
46
  *
49
47
  * OPTICAL PADDING (unbounded content containers)
50
48
  * ==============================================
@@ -56,7 +54,7 @@
56
54
  * grow linearly while text barely moves (12px at dense, 14 above), so
57
55
  * whitespace must grow SUPER-linearly
58
56
  * to hold the same perceived balance. (The tokens that already feel right
59
- * across modes — inset-md 5→17, content-p 16→48 — are exactly the convex
57
+ * across modes — inset-control 6→17, pad-content 16→48 — are exactly the convex
60
58
  * ones; the cramped ones — card's old sp/2 = 4→8 — are the linear ones.)
61
59
  *
62
60
  * The optical unit is the mean of the linear and quadratic scales:
@@ -73,17 +71,17 @@
73
71
  * --era-pad-card so 8 / 11.25 / 15 / 24 — cards, card sections, dialogs, popovers
74
72
  * --era-pad-content 2·so 16 / 22.5 / 30 / 48 — full content panels (docs sections)
75
73
  *
76
- * Whitespace share rises with mode: pad-md / h-control = 0.33 dense → 0.50 touch.
74
+ * Whitespace share rises with mode: pad-card / h-control = 0.33 dense → 0.50 touch.
77
75
  *
78
76
  * IDENTITY USED AT NESTING SITES
79
77
  * ==============================
80
78
  *
81
- * A flush pill at an md-container edge needs −(inset-xxs) margin so
79
+ * A flush pill at a control-container edge needs −(inset-pill) margin so
82
80
  * the horizontal gap equals the vertical concentric gap. Proof:
83
- * inset-mdxxs-inset-md
84
- * = (md − text)/2 − (mdxxs)/2
85
- * = (xxs − text)/2
86
- * = inset-xxs.
81
+ * inset-controlpill-inset-control
82
+ * = (control − text)/2 − (controlpill)/2
83
+ * = (pill − text)/2
84
+ * = inset-pill.
87
85
  *
88
86
  * RADII (concentric progression, sp-derived)
89
87
  * ==========================================
@@ -146,12 +144,13 @@
146
144
  --era-glyph-stroke-width: 1.75; /* Lucide SVG stroke, tuned to sit beside 14px body text */
147
145
 
148
146
  /* Icon-button glyph sub-tiers. The glyph normally IS the icon tier (h-icon, the
149
- sp+6 legibility floor) — that's what every text button's icon and every md/sm
150
- icon button uses. But when the BOX collapses toward the icon tier — xxs, and
151
- xs/icon where box == h-icon — an h-icon glyph would fill it edge to edge. So the
152
- glyph steps below the floor by 2px per tier, preserving a concentric gap:
153
- sp+4 in an xxs box (pad 3), sp+2 in an xs box (pad 2). The dense pads then run
154
- 2·3·4·5 across icon·pill·chip·control — a unit step off the floor, per Button. */
147
+ sp+6 legibility floor) — that's what every text button's icon and every
148
+ control/chip icon button uses. But when the BOX collapses toward the icon tier
149
+ — a pill box, and an icon box where box == h-icon — an h-icon glyph would fill
150
+ it edge to edge. So the glyph steps below the floor by 2px per tier, preserving
151
+ a concentric gap: sp+4 in a pill box (pad 3), sp+2 in an icon box (pad 2). The
152
+ dense pads then run 2·3·4·5 across icon·pill·chip·control — a unit step off the
153
+ floor, per Button. */
155
154
  --era-glyph-pill: calc(
156
155
  var(--era-sp) + 0.25rem
157
156
  ); /* 12/14/16 = sp + 4 (glyph in a pill-tier button) */
@@ -211,33 +210,33 @@
211
210
  --era-pill-px: max(0px, calc((var(--era-h-pill) - 0.6 * var(--era-text)) / 2)); /* 5.4/5.8/6.8 */
212
211
  --era-chip-px: max(0px, calc((var(--era-h-chip) - 0.6 * var(--era-text)) / 2)); /* 7.4/8.8/10.8 */
213
212
 
214
- /* Text-field side padding — the horizontal room where typed text starts in an
215
- md-tier input (Input, Textarea, combobox/command inputs, date/time fields).
216
- Purpose-named, NOT "pill": an input is not a pill, and it lives on the md
217
- tier while the pill tokens are sm/xxs.
213
+ /* Text-field side padding — the horizontal room where typed text starts in a
214
+ control-tier input (Input, Textarea, combobox/command inputs, date/time fields).
215
+ Purpose-named, NOT "pill": an input is not a pill, and it lives on the control
216
+ tier while the pill tokens are chip/pill.
218
217
 
219
- It borrows pill-sm's glyph-advance derivation (the right shape for a STRING
220
- of text vs a single centered glyph) but floors it at 0.5rem. pill-sm is
221
- algebraically sp − 1.2px, so it grows slower than the md height (3·sp) and is
222
- proportionally TIGHTEST at dense — 6.8px in a 24px box read cramped. The
223
- floor only bites at dense (pill-sm ≥ 8.8 at every looser mode), lifting dense
218
+ It borrows --era-chip-px's glyph-advance derivation (the right shape for a
219
+ STRING of text vs a single centred glyph) but floors it at 0.5rem. chip-px is
220
+ algebraically sp − 1.2px, so it grows slower than the control height (3·sp) and
221
+ is proportionally TIGHTEST at dense — 6.8px in a 24px box read cramped. The
222
+ floor only bites at dense (chip-px ≥ 8.8 at every looser mode), lifting dense
224
223
  to 8px and leaving balanced/spacious/touch exactly as they were. */
225
224
  --era-field-px: max(var(--era-chip-px), 0.5rem); /* 8/8.8/10.8/14.8 — floor lifts dense only */
226
225
 
227
226
  /* Concentric insets — center a smaller tier element inside a larger one. */
228
- /* --era-icon-inset-chip stays at sp/2 by construction (smxs = sp), which is
229
- what switch thumbs and KV icons rely on. --era-pill-inset-control is the "pill
230
- inside a button" concentric gap used both vertically (trigger padding)
227
+ /* --era-icon-inset-chip stays at sp/2 by construction (chipicon = sp), which
228
+ is what switch thumbs and KV icons rely on. --era-pill-inset-control is the
229
+ "pill inside a button" concentric gap used both vertically (trigger padding)
231
230
  and horizontally (inter-chip gap inside multi-select Value). */
232
231
  --era-icon-inset-chip: calc((var(--era-h-chip) - var(--era-h-icon)) / 2); /* 4/5/6 = sp/2 */
233
232
  --era-icon-inset-pill: calc(
234
233
  (var(--era-h-pill) - var(--era-h-icon)) / 2
235
- ); /* 2 (flat) — center an xs element (dismiss button) inside xxs */
234
+ ); /* 2 (flat) — centre an icon-tier element (dismiss button) inside a pill */
236
235
  --era-pill-inset-control: calc(
237
236
  (var(--era-h-control) - var(--era-h-pill)) / 2
238
237
  ); /* 3/5/7 = sp − 5 */
239
- /* Centre an pill inside an sm one — a dismiss chip in a standalone chip, an
240
- xxs control in an sm bar.
238
+ /* Centre a pill inside a chip — a dismiss chip in a standalone chip, a
239
+ pill-tier control in a chip-tier bar.
241
240
  It reads like a duplicate of --era-inset-pill (3/3/4/6) and used to BE one:
242
241
  while text was 14px everywhere, (h-chip − h-pill)/2 and (h-pill − text)/2 were
243
242
  the same number, and callers borrowed the text inset rather than declaring
@@ -246,16 +245,18 @@
246
245
  --era-pill-inset-chip: calc((var(--era-h-chip) - var(--era-h-pill)) / 2); /* 2/3/4/6 */
247
246
  --era-icon-inset-control: calc(
248
247
  (var(--era-h-control) - var(--era-h-icon)) / 2
249
- ); /* 5/7/9 = sp − 3 — center an xs control inside an md bar (= rd-md − rd-xs) */
248
+ ); /* 5/7/9 = sp − 3 — centre an icon control inside a control (= rd-control − rd-icon) */
250
249
  --era-icon-inset-bar: calc(
251
250
  (var(--era-h-bar) - var(--era-h-icon)) / 2
252
- ); /* 9/12/15 — center an xs control inside an lg bar (= rd-lg − rd-xs) */
251
+ ); /* 9/12/15 — centre an icon control inside a bar (= rd-bar − rd-icon) */
253
252
  --era-chip-inset-control: calc(
254
253
  (var(--era-h-control) - var(--era-h-chip)) / 2
255
- ); /* 1/2/3 — center an sm control inside an md field (= rd-md − rd-sm), e.g. the date picker's trailing trigger */
254
+ ); /* 1/2/3 — centre a chip control inside a control field (= rd-control − rd-chip),
255
+ e.g. the date picker's trailing trigger */
256
256
  --era-chip-inset-bar: calc(
257
257
  (var(--era-h-bar) - var(--era-h-chip)) / 2
258
- ); /* 5/7/9 = sp − 3 — inset an sm panel (drag bar) concentrically in an lg pane (= rd-lg − rd-sm) */
258
+ ); /* 5/7/9 = sp − 3 — inset a chip panel (drag bar) concentrically in a bar-tier
259
+ pane (= rd-bar − rd-chip) */
259
260
 
260
261
  /* Roundness ladder — golden base + concentric progression, both gated by scale. */
261
262
  /* rd-T = scale · (R + (h-T − h-icon) / 2). R = sp / φ². max(0px, …) clamps tier
@@ -269,11 +270,11 @@
269
270
  a center of curvature with zero arithmetic.
270
271
 
271
272
  The one trap: a CONTAINER's radius follows its corner REGION's tier, never
272
- its overall bulk. A pane capped by an sm handle bar (xs controls at
273
- xs-inset-sm) is an sm corner region → rd-sm, however large the pane.
273
+ its overall bulk. A pane capped by a chip-tier handle bar (icon controls at
274
+ icon-inset-chip) is a chip corner region → rd-chip, however large the pane.
274
275
  Choosing rd by visual importance is how concentricity breaks. Precedents:
275
- Chip (sm shell + xs dismiss), Bar (h-T ↔ rd-T), Pane.Root (rd-sm), menu
276
- panels (rd-md, items at era-item-rd = rd-md − panel-inset). */
276
+ Chip (chip shell + icon dismiss), Bar (h-T ↔ rd-T), Pane.Root (rd-chip), menu
277
+ panels (rd-control, items at era-item-rd = rd-control − panel-inset). */
277
278
  --era-roundness: calc(var(--era-roundness-scale) * var(--era-sp) / 2.618);
278
279
  --era-rd-icon: max(0px, var(--era-roundness));
279
280
  --era-rd-pill: max(
@@ -384,7 +385,7 @@
384
385
  }
385
386
 
386
387
  [data-mode='touch'] {
387
- --era-sp: 1rem; /* 16px — md tier becomes 48px to clear the 44px tap-target floor */
388
+ --era-sp: 1rem; /* 16px — the control tier becomes 48px, clearing the 44px tap-target floor */
388
389
  --era-density: 2;
389
390
  }
390
391
 
@@ -1,14 +1,15 @@
1
1
  <script lang="ts" module>
2
2
  import { cn, tv } from '../../utils/index.js';
3
3
 
4
- // A Bar is a flex container sized to a tier height, holding one-tier-smaller
5
- // controls with the even sp/2 gap. The height scales with density; pick the
6
- // tier that fits the content:
7
- // lg (default) — full toolbar / page header of md controls (32/40/48/64)
8
- // md — compact toolbar (24/30/36/48)
9
- // sm — title / handle bar of xs icons+buttons (22/26/30/38)
10
- // Radius stays concentric to the height; gap/px are sp/2 (xs-inset-sm),
11
- // the even gap for one-tier-smaller content at every size.
4
+ // A Bar is a flex container sized to a tier height, holding smaller controls
5
+ // at the even concentric gap. The height scales with density; pick the tier
6
+ // that fits the content:
7
+ // bar (default) — full toolbar / page header of controls (32/40/48/64)
8
+ // control — compact toolbar (24/30/36/48)
9
+ // chip — title / handle bar of icon buttons (22/26/30/38)
10
+ // Radius stays concentric to the height. The gap and the wall are NOT one
11
+ // number across the sizes see the content variant below for why, and what
12
+ // each pairing actually resolves to.
12
13
  export const barVariants = tv({
13
14
  base: 'flex items-center leading-none',
14
15
  variants: {
@@ -25,14 +26,34 @@
25
26
  * had each restated the two-tier value by hand, and the geometry spec had
26
27
  * been rewritten twice chasing which token they were restating.
27
28
  *
28
- * - `tier` (default) — one tier smaller, the pairing above. chip⊃icon, control⊃chip
29
- * and bar⊃control all equal sp/2, which is why one token covers every size.
30
- * - `pill` — the compact pairing: pills and fields (a filter beside an
31
- * xxs icon button, a title bar). Two tiers down, so the inset differs
32
- * per size and comes from the compounds below.
29
+ * - `tier` (default) — one tier smaller, the pairing above. Each size gets
30
+ * its OWN token, from the compounds below.
31
+ * - `pill` — the compact pairing: pills and fields (a filter beside a
32
+ * pill-tier icon button, a title bar). Two tiers down, so the inset
33
+ * differs per size and also comes from the compounds.
34
+ *
35
+ * THIS USED TO BE ONE CLASS FOR ALL THREE SIZES — `px-icon-inset-chip`,
36
+ * on the stated reasoning that "chip⊃icon, control⊃chip and bar⊃control
37
+ * all equal sp/2". Two of those three do. control⊃chip does not:
38
+ *
39
+ * chip ⊃ icon = ((2sp+6) − (sp+6))/2 = sp/2 4/5/6
40
+ * bar ⊃ control = (4sp − 3sp)/2 = sp/2 4/5/6
41
+ * control ⊃ chip = (3sp − (2sp+6))/2 = (sp−6)/2 1/2/3 ← not sp/2
42
+ *
43
+ * chip is the one rung that is not a clean sp multiple (it carries the
44
+ * same +6 legibility floor as icon), so the difference cancels for the
45
+ * pair that spans it and survives for the pair that does not. A control
46
+ * bar therefore padded 4px horizontally while items-center centred its
47
+ * children to 1px — the wall was four times the gap it was meant to
48
+ * match, which is what a reader sees as "more space on the left".
49
+ *
50
+ * No audit caught it: layout/wall-matches-gap compares the horizontal
51
+ * wall to the horizontal gap (4 and 4 — even), and
52
+ * layout/even-container-padding compares top to bottom (equal). Nothing
53
+ * compared ACROSS the axes. layout/wall-is-isotropic now does.
33
54
  */
34
55
  content: {
35
- tier: 'gap-icon-inset-chip px-icon-inset-chip',
56
+ tier: '',
36
57
  pill: ''
37
58
  },
38
59
  /**
@@ -59,6 +80,28 @@
59
80
  }
60
81
  },
61
82
  compoundVariants: [
83
+ // content="tier" — one tier down, each pairing named for itself.
84
+ // chip ⊃ icon = sp/2.
85
+ {
86
+ size: 'chip',
87
+ content: 'tier',
88
+ class: 'gap-icon-inset-chip px-icon-inset-chip'
89
+ },
90
+ // control ⊃ chip = (sp−6)/2 — the tight-but-present rung, 1/2/3.
91
+ {
92
+ size: 'control',
93
+ content: 'tier',
94
+ class: 'gap-chip-inset-control px-chip-inset-control'
95
+ },
96
+ // bar ⊃ control = sp/2, which is exactly --era-gap. era already calls
97
+ // this pairing "the gutter" (see the rounded-row derivation in
98
+ // styles/index.css), so it takes that name rather than borrowing
99
+ // icon-inset-chip's — same number, but a reader can tell why.
100
+ {
101
+ size: 'bar',
102
+ content: 'tier',
103
+ class: 'gap-gutter px-gutter'
104
+ },
62
105
  // control ⊃ pill = (3sp − sp − 10)/2 — the named token.
63
106
  {
64
107
  size: 'control',
@@ -12,14 +12,34 @@ export declare const barVariants: import("tailwind-variants").TVReturnType<{
12
12
  * had each restated the two-tier value by hand, and the geometry spec had
13
13
  * been rewritten twice chasing which token they were restating.
14
14
  *
15
- * - `tier` (default) — one tier smaller, the pairing above. chip⊃icon, control⊃chip
16
- * and bar⊃control all equal sp/2, which is why one token covers every size.
17
- * - `pill` — the compact pairing: pills and fields (a filter beside an
18
- * xxs icon button, a title bar). Two tiers down, so the inset differs
19
- * per size and comes from the compounds below.
15
+ * - `tier` (default) — one tier smaller, the pairing above. Each size gets
16
+ * its OWN token, from the compounds below.
17
+ * - `pill` — the compact pairing: pills and fields (a filter beside a
18
+ * pill-tier icon button, a title bar). Two tiers down, so the inset
19
+ * differs per size and also comes from the compounds.
20
+ *
21
+ * THIS USED TO BE ONE CLASS FOR ALL THREE SIZES — `px-icon-inset-chip`,
22
+ * on the stated reasoning that "chip⊃icon, control⊃chip and bar⊃control
23
+ * all equal sp/2". Two of those three do. control⊃chip does not:
24
+ *
25
+ * chip ⊃ icon = ((2sp+6) − (sp+6))/2 = sp/2 4/5/6
26
+ * bar ⊃ control = (4sp − 3sp)/2 = sp/2 4/5/6
27
+ * control ⊃ chip = (3sp − (2sp+6))/2 = (sp−6)/2 1/2/3 ← not sp/2
28
+ *
29
+ * chip is the one rung that is not a clean sp multiple (it carries the
30
+ * same +6 legibility floor as icon), so the difference cancels for the
31
+ * pair that spans it and survives for the pair that does not. A control
32
+ * bar therefore padded 4px horizontally while items-center centred its
33
+ * children to 1px — the wall was four times the gap it was meant to
34
+ * match, which is what a reader sees as "more space on the left".
35
+ *
36
+ * No audit caught it: layout/wall-matches-gap compares the horizontal
37
+ * wall to the horizontal gap (4 and 4 — even), and
38
+ * layout/even-container-padding compares top to bottom (equal). Nothing
39
+ * compared ACROSS the axes. layout/wall-is-isotropic now does.
20
40
  */
21
41
  content: {
22
- tier: "gap-icon-inset-chip px-icon-inset-chip";
42
+ tier: "";
23
43
  pill: "";
24
44
  };
25
45
  /**
@@ -58,14 +78,34 @@ export declare const barVariants: import("tailwind-variants").TVReturnType<{
58
78
  * had each restated the two-tier value by hand, and the geometry spec had
59
79
  * been rewritten twice chasing which token they were restating.
60
80
  *
61
- * - `tier` (default) — one tier smaller, the pairing above. chip⊃icon, control⊃chip
62
- * and bar⊃control all equal sp/2, which is why one token covers every size.
63
- * - `pill` — the compact pairing: pills and fields (a filter beside an
64
- * xxs icon button, a title bar). Two tiers down, so the inset differs
65
- * per size and comes from the compounds below.
81
+ * - `tier` (default) — one tier smaller, the pairing above. Each size gets
82
+ * its OWN token, from the compounds below.
83
+ * - `pill` — the compact pairing: pills and fields (a filter beside a
84
+ * pill-tier icon button, a title bar). Two tiers down, so the inset
85
+ * differs per size and also comes from the compounds.
86
+ *
87
+ * THIS USED TO BE ONE CLASS FOR ALL THREE SIZES — `px-icon-inset-chip`,
88
+ * on the stated reasoning that "chip⊃icon, control⊃chip and bar⊃control
89
+ * all equal sp/2". Two of those three do. control⊃chip does not:
90
+ *
91
+ * chip ⊃ icon = ((2sp+6) − (sp+6))/2 = sp/2 4/5/6
92
+ * bar ⊃ control = (4sp − 3sp)/2 = sp/2 4/5/6
93
+ * control ⊃ chip = (3sp − (2sp+6))/2 = (sp−6)/2 1/2/3 ← not sp/2
94
+ *
95
+ * chip is the one rung that is not a clean sp multiple (it carries the
96
+ * same +6 legibility floor as icon), so the difference cancels for the
97
+ * pair that spans it and survives for the pair that does not. A control
98
+ * bar therefore padded 4px horizontally while items-center centred its
99
+ * children to 1px — the wall was four times the gap it was meant to
100
+ * match, which is what a reader sees as "more space on the left".
101
+ *
102
+ * No audit caught it: layout/wall-matches-gap compares the horizontal
103
+ * wall to the horizontal gap (4 and 4 — even), and
104
+ * layout/even-container-padding compares top to bottom (equal). Nothing
105
+ * compared ACROSS the axes. layout/wall-is-isotropic now does.
66
106
  */
67
107
  content: {
68
- tier: "gap-icon-inset-chip px-icon-inset-chip";
108
+ tier: "";
69
109
  pill: "";
70
110
  };
71
111
  /**
@@ -104,14 +144,34 @@ export declare const barVariants: import("tailwind-variants").TVReturnType<{
104
144
  * had each restated the two-tier value by hand, and the geometry spec had
105
145
  * been rewritten twice chasing which token they were restating.
106
146
  *
107
- * - `tier` (default) — one tier smaller, the pairing above. chip⊃icon, control⊃chip
108
- * and bar⊃control all equal sp/2, which is why one token covers every size.
109
- * - `pill` — the compact pairing: pills and fields (a filter beside an
110
- * xxs icon button, a title bar). Two tiers down, so the inset differs
111
- * per size and comes from the compounds below.
147
+ * - `tier` (default) — one tier smaller, the pairing above. Each size gets
148
+ * its OWN token, from the compounds below.
149
+ * - `pill` — the compact pairing: pills and fields (a filter beside a
150
+ * pill-tier icon button, a title bar). Two tiers down, so the inset
151
+ * differs per size and also comes from the compounds.
152
+ *
153
+ * THIS USED TO BE ONE CLASS FOR ALL THREE SIZES — `px-icon-inset-chip`,
154
+ * on the stated reasoning that "chip⊃icon, control⊃chip and bar⊃control
155
+ * all equal sp/2". Two of those three do. control⊃chip does not:
156
+ *
157
+ * chip ⊃ icon = ((2sp+6) − (sp+6))/2 = sp/2 4/5/6
158
+ * bar ⊃ control = (4sp − 3sp)/2 = sp/2 4/5/6
159
+ * control ⊃ chip = (3sp − (2sp+6))/2 = (sp−6)/2 1/2/3 ← not sp/2
160
+ *
161
+ * chip is the one rung that is not a clean sp multiple (it carries the
162
+ * same +6 legibility floor as icon), so the difference cancels for the
163
+ * pair that spans it and survives for the pair that does not. A control
164
+ * bar therefore padded 4px horizontally while items-center centred its
165
+ * children to 1px — the wall was four times the gap it was meant to
166
+ * match, which is what a reader sees as "more space on the left".
167
+ *
168
+ * No audit caught it: layout/wall-matches-gap compares the horizontal
169
+ * wall to the horizontal gap (4 and 4 — even), and
170
+ * layout/even-container-padding compares top to bottom (equal). Nothing
171
+ * compared ACROSS the axes. layout/wall-is-isotropic now does.
112
172
  */
113
173
  content: {
114
- tier: "gap-icon-inset-chip px-icon-inset-chip";
174
+ tier: "";
115
175
  pill: "";
116
176
  };
117
177
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signal9/era-ui",
3
- "version": "20.0.0",
3
+ "version": "21.0.0",
4
4
  "scripts": {
5
5
  "dev": "vite dev --host",
6
6
  "build": "vite build && npm run prepack",
@@ -25,9 +25,10 @@
25
25
  "check:classes": "node --experimental-strip-types scripts/check-classes.ts",
26
26
  "check:twmerge": "node --experimental-strip-types scripts/check-twmerge.ts",
27
27
  "build:llm-docs": "node --experimental-strip-types scripts/build-llm-docs.ts",
28
- "prebuild": "npm run check:surfaces && npm run check:tokens && npm run check:routes && npm run check:canonical && npm run check:layers && npm run check:themes && npm run check:classes && npm run check:twmerge && npm run build:llm-docs",
29
- "validate": "npm run check && npm run lint && npm run format:check && npm run check:surfaces && npm run check:tokens && npm run check:routes && npm run check:canonical && npm run check:layers && npm run check:themes && npm run check:classes && npm run check:twmerge",
30
- "test": "npm run test:visual"
28
+ "prebuild": "npm run check:surfaces && npm run check:tokens && npm run check:routes && npm run check:canonical && npm run check:layers && npm run check:themes && npm run check:classes && npm run check:twmerge && npm run check:doc-tokens && npm run build:llm-docs",
29
+ "validate": "npm run check && npm run lint && npm run format:check && npm run check:surfaces && npm run check:tokens && npm run check:routes && npm run check:canonical && npm run check:layers && npm run check:themes && npm run check:classes && npm run check:twmerge && npm run check:doc-tokens",
30
+ "test": "npm run test:visual",
31
+ "check:doc-tokens": "node --experimental-strip-types scripts/check-doc-tokens.ts"
31
32
  },
32
33
  "files": [
33
34
  "dist",
package/skill/SKILL.md CHANGED
@@ -64,13 +64,13 @@ Named for the ROLE each rung plays, never for its size — a scale whose names
64
64
  can invert is a scale nobody reads correctly (this library once shipped an
65
65
  `xxs` that was bigger than its `xs`).
66
66
 
67
- | Tier | Height | Square | Radius | Use for |
68
- | --------- | ----------- | ------------- | ---------------- | ------------------------------------------- |
69
- | `icon` | `h-icon` | `size-icon` | `rounded-icon` | a lucide glyph, switch thumbs |
70
- | `pill` | `h-pill` | `size-pill` | `rounded-pill` | a badge/chip nested INSIDE a control |
71
- | `chip` | `h-chip` | `size-chip` | `rounded-chip` | standalone pills, switch tracks, KV |
72
- | `control` | `h-control` | `size-control`| `rounded-control`| buttons, inputs, selects, triggers |
73
- | `bar` | `h-bar` | `size-bar` | `rounded-bar` | bars, containers, dialog headers |
67
+ | Tier | Height | Square | Radius | Use for |
68
+ | --------- | ----------- | -------------- | ----------------- | ------------------------------------ |
69
+ | `icon` | `h-icon` | `size-icon` | `rounded-icon` | a lucide glyph, switch thumbs |
70
+ | `pill` | `h-pill` | `size-pill` | `rounded-pill` | a badge/chip nested INSIDE a control |
71
+ | `chip` | `h-chip` | `size-chip` | `rounded-chip` | standalone pills, switch tracks, KV |
72
+ | `control` | `h-control` | `size-control` | `rounded-control` | buttons, inputs, selects, triggers |
73
+ | `bar` | `h-bar` | `size-bar` | `rounded-bar` | bars, containers, dialog headers |
74
74
 
75
75
  `min-h-*`, `min-w-*` and `w-*` exist across the same rungs. There is no escape
76
76
  hatch here any more: `min-w-control` is a real class, because role names collide
@@ -90,8 +90,11 @@ corners axis.
90
90
  give EVERY element the radius of its OWN height tier, center children with
91
91
  the matching concentric inset, and nested corners share a center of
92
92
  curvature automatically. The concentric insets are advanced enough to stay
93
- token-form: `--era-xs-inset-sm` (xs inside sm, = sp/2), `--era-xxs-inset-md`,
94
- `--era-xs-inset-md`, `--era-xs-inset-lg`, `--era-sm-inset-lg`.
93
+ token-form, and each is named for the pair it centres:
94
+ `--era-icon-inset-chip` (an icon inside a chip, = sp/2),
95
+ `--era-icon-inset-pill`, `--era-pill-inset-control`, `--era-pill-inset-chip`,
96
+ `--era-icon-inset-control`, `--era-icon-inset-bar`, `--era-chip-inset-control`,
97
+ `--era-chip-inset-bar`.
95
98
 
96
99
  **The trap**: a container's radius follows its corner REGION's tier, never its
97
100
  overall bulk. A large panel whose corners are formed by a chip-tier handle bar