@signal9/era-ui 30.8.0 → 31.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.
Files changed (83) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/ai/conversations/conversations-item.svelte +1 -1
  3. package/dist/ai/plan/plan-header.svelte +1 -1
  4. package/dist/ai/prompt-input/prompt-input-attachments.svelte +2 -6
  5. package/dist/ai/swarm/swarm-node.svelte +1 -1
  6. package/dist/ai/todo/todo-header.svelte +1 -1
  7. package/dist/ai/todo/todo-item.svelte +1 -1
  8. package/dist/ai/tool/tool-chip.svelte +1 -1
  9. package/dist/ai/web-preview/web-preview.svelte +1 -1
  10. package/dist/dev/audit/audits/wall-is-isotropic.js +1 -1
  11. package/dist/docs/css-utilities.js +7 -3
  12. package/dist/era-ui.css +1 -1
  13. package/dist/generated-docs/badge.md +1 -1
  14. package/dist/generated-docs/bar.md +22 -4
  15. package/dist/generated-docs/button.md +38 -4
  16. package/dist/generated-docs/card.md +35 -5
  17. package/dist/generated-docs/chip.md +16 -0
  18. package/dist/generated-docs/llms-full.txt +310 -49
  19. package/dist/generated-docs/llms.txt +4 -3
  20. package/dist/generated-docs/manifest.json +45 -21
  21. package/dist/generated-docs/meter.md +79 -1
  22. package/dist/generated-docs/nav.md +8 -1
  23. package/dist/generated-docs/separator.md +18 -0
  24. package/dist/generated-docs/utilities.json +32 -9
  25. package/dist/generated-docs/utilities.md +89 -30
  26. package/dist/styles/density.css +15 -25
  27. package/dist/styles/index.css +32 -27
  28. package/dist/ui/accordion/accordion-trigger.svelte +1 -1
  29. package/dist/ui/badge/badge.svelte +8 -2
  30. package/dist/ui/badge/badge.svelte.d.ts +1 -1
  31. package/dist/ui/bar/bar.svelte +95 -26
  32. package/dist/ui/bar/bar.svelte.d.ts +71 -31
  33. package/dist/ui/button/button.svelte +5 -0
  34. package/dist/ui/button/button.svelte.d.ts +2 -0
  35. package/dist/ui/button/variants.js +4 -9
  36. package/dist/ui/calendar/calendar-day.svelte +1 -1
  37. package/dist/ui/card/card.svelte +22 -9
  38. package/dist/ui/card/card.svelte.d.ts +19 -7
  39. package/dist/ui/chip/chip.svelte +5 -2
  40. package/dist/ui/chip/chip.svelte.d.ts +6 -6
  41. package/dist/ui/collapsible/collapsible-trigger.svelte +1 -1
  42. package/dist/ui/combobox/combobox-trigger.svelte +1 -1
  43. package/dist/ui/command/command-item.svelte +1 -1
  44. package/dist/ui/command/command-link-item.svelte +1 -1
  45. package/dist/ui/command/command-root.svelte +3 -5
  46. package/dist/ui/date-field/date-field-segment.svelte +1 -1
  47. package/dist/ui/date-picker/date-picker-day.svelte +1 -1
  48. package/dist/ui/date-picker/date-picker-segment.svelte +1 -1
  49. package/dist/ui/date-picker/date-picker-trigger.svelte +1 -1
  50. package/dist/ui/date-range-field/date-range-field-segment.svelte +1 -1
  51. package/dist/ui/date-range-picker/date-range-picker-day.svelte +1 -1
  52. package/dist/ui/date-range-picker/date-range-picker-segment.svelte +1 -1
  53. package/dist/ui/date-range-picker/date-range-picker-trigger.svelte +1 -1
  54. package/dist/ui/dialog/dialog-content.svelte +19 -1
  55. package/dist/ui/dropdown-menu/dropdown-menu-trigger.svelte +1 -1
  56. package/dist/ui/file-upload/file-upload.svelte +1 -1
  57. package/dist/ui/input/variants.d.ts +6 -6
  58. package/dist/ui/input/variants.js +2 -2
  59. package/dist/ui/kv/kv.svelte +1 -1
  60. package/dist/ui/menubar/menubar-root.svelte +1 -1
  61. package/dist/ui/menubar/menubar-trigger.svelte +1 -1
  62. package/dist/ui/nav/nav-bar.svelte +9 -4
  63. package/dist/ui/nav/nav-bar.svelte.d.ts +2 -1
  64. package/dist/ui/pane/pane-root.svelte +3 -12
  65. package/dist/ui/pane/pane-tab.svelte +1 -1
  66. package/dist/ui/select/select-trigger.svelte +1 -1
  67. package/dist/ui/slider/slider-thumb.svelte +1 -1
  68. package/dist/ui/step/step-summary.svelte +1 -1
  69. package/dist/ui/switch/switch.svelte +2 -2
  70. package/dist/ui/time-field/time-field-segment.svelte +1 -1
  71. package/dist/ui/time-range-field/time-range-field-segment.svelte +1 -1
  72. package/dist/ui/timeline/timeline-step.svelte +1 -1
  73. package/dist/ui/toggle-group/toggle-group-item.svelte +2 -2
  74. package/dist/ui/toggle-group/toggle-group-item.svelte.d.ts +6 -6
  75. package/dist/ui/toggle-group/toggle-group-root.svelte +1 -2
  76. package/dist/ui/toggle-group/toggle-group-root.svelte.d.ts +3 -3
  77. package/dist/ui/tooltip/tooltip-content.svelte +1 -1
  78. package/dist/ui/tree/tree-item.svelte +1 -1
  79. package/dist/ui/video-player/video-player.svelte +1 -1
  80. package/dist/utils/tw-merge-config.d.ts +3 -0
  81. package/dist/utils/tw-merge-config.js +4 -0
  82. package/package.json +4 -3
  83. package/skill/SKILL.md +17 -17
@@ -64,8 +64,9 @@ Classes, not components — they need no import beyond the stylesheet. Reach for
64
64
  - `era-token-ink` — the ink centring for a token pill — a badge, a KV value, any short caps-or-digits label in a fixed-height box, CENTRED horizontally and with no icon sibling. For a left-aligned sentence-case label use era-label-ink; when an icon really must sit beside the text, see the note there.
65
65
  - `era-label-ink` — the ink centring for a fixed-height control holding ONE line of sentence-case text and no icon — a menu item, a nav trigger, a button label. Never alongside `truncate` (the trimmed box becomes the clip).
66
66
  - `glass-blur` — the backdrop blur for a floating panel (popover, dialog, taskbar) — it applies only inside a data-surface="glass" subtree and is inert elsewhere, so it is safe to leave on a panel that renders under every surface.
67
- - `rounded-row` the radius for a row inside a container that pads it by the gutter a nav rail, a command list, any scrolling list of controls. For a row in a menu PANEL, whose inset is the surface-owned frame rather than the gutter, use rounded-menu-item.
67
+ - `tier-icon · tier-pill · tier-chip · tier-control · tier-bar` BOUNDED GEOMETRYheight and radius are one decision.
68
68
  - `h-line` — the height of one line of the element's own text — a skeleton standing in for a line, a spacer that must match a line box.
69
+ - `era-track-well` — instead of `shadow-well` on a track whose value is painted by a child that covers the box — a progress/meter/slider track, a custom gauge. For a well holding ordinary content, `shadow-well` is still right.
69
70
  - `era-skeleton-pulse` — Skeleton's pulse, on the MOTION AXIS.
70
71
  - `era-shimmer` — the in-progress text treatment — put it on a "working…" / "thinking…" label while a task streams, instead of a spinner beside the text.
71
72
  - `era-collapse` — the height sweep for a bits-ui Collapsible.Content — put it on the Content element itself (it carries its own overflow: hidden).
@@ -86,12 +87,12 @@ Classes, not components — they need no import beyond the stylesheet. Reach for
86
87
  - `bg-well · text-well · border-well · bg-elevated · text-elevated · border-elevated · bg-highlight · text-highlight · … (12 in all)` — Surface fills — bg-well is the surface's own resting fill (pairs with
87
88
  - `p-gutter · px-gutter · py-gutter · gap-gutter · p-panel · px-panel · py-panel · gap-panel · … (24 in all)` — THE SPACING SCALE — one entry per token, from which Tailwind derives the
88
89
  - `p-inset-pill · px-inset-pill · py-inset-pill · gap-inset-pill · p-inset-chip · px-inset-chip · py-inset-chip · gap-inset-chip · … (12 in all)` — text-symmetric content padding — (tier − text)/2, the inset that vertically centres body text in its tier; px-inset-control is a text button's side padding, p-inset-control a code block's.
89
- - `p-icon-inset-pill · px-icon-inset-pill · py-icon-inset-pill · gap-icon-inset-pill · p-icon-inset-chip · px-icon-inset-chip · py-icon-inset-chip · gap-icon-inset-chip · … (36 in all)` — the concentric inset for a nested pair — (h-container − h-child)/2, the gap that makes a child control sit evenly inside its container, and the radius step that keeps their corners concentric. Named for the pair it joins: gap-icon-inset-control seats an icon in a control.
90
+ - `p-icon-inset-pill · px-icon-inset-pill · py-icon-inset-pill · gap-icon-inset-pill · p-icon-inset-chip · px-icon-inset-chip · py-icon-inset-chip · gap-icon-inset-chip · … (40 in all)` — the concentric inset for a nested pair — (h-container − h-child)/2, the gap that makes a child control sit evenly inside its container, and the radius step that keeps their corners concentric. Named for the pair it joins: gap-icon-inset-control seats an icon in a control.
90
91
  - `p-glyph-icon · px-glyph-icon · py-glyph-icon · gap-glyph-icon · p-glyph-pill · px-glyph-pill · py-glyph-pill · gap-glyph-pill` — the glyph size for a tier — the INK inside the box, not the box (size-glyph-pill is the dismiss × in a pill, size-pill is the pill).
91
92
  - `bg-field · text-field · border-field · bg-field-highlight · text-field-highlight · border-field-highlight` — the recessed field fill — bg-field is a text input's resting well, bg-field-highlight its focused/hovered face.
92
93
  - `ease-base` — Motion — bare transition utilities default to the motion axis, so
93
94
  - `bg-divider · text-divider · border-divider · bg-divider-faded · text-divider-faded · border-divider-faded · bg-background · text-background · … (66 in all)` — Surface divider colors: border-divider, bg-divider-faded, …
94
- - `bg-fill · text-fill · border-fill · bg-link · text-link · border-link · bg-primary · text-primary · … (33 in all)` — The standing pill fill — --era-fill, the latched counterpart of the
95
+ - `bg-fill · text-fill · border-fill · bg-link · text-link · border-link · bg-primary · text-primary · … (39 in all)` — The standing pill fill — --era-fill, the latched counterpart of the
95
96
 
96
97
  Full reference (what each emits, when to use it): `/utilities.md`. Machine-readable list: `/utilities.json`.
97
98
 
@@ -153,7 +154,7 @@ The CSS classes era ships alongside the components — links, ink-centred text,
153
154
 
154
155
  ## Overview
155
156
 
156
- era's styling API is pure class names — write `h-control rounded-control px-gutter shadow`,
157
+ era's styling API is pure class names — write `tier-control px-gutter shadow`,
157
158
  never the `h-(--era-h-control)` var-class spelling. Everything below rides along
158
159
  with the stylesheet and needs no import:
159
160
 
@@ -162,7 +163,7 @@ import "@signal9/era-ui/css";
162
163
  ```
163
164
 
164
165
  ```svelte
165
- <button class="h-control rounded-control px-inset-control shadow text-body">era styled</button>
166
+ <button class="tier-control px-inset-control shadow text-body">era styled</button>
166
167
  ```
167
168
 
168
169
  Alongside the `@utility` classes referenced below, era registers named theme
@@ -172,6 +173,7 @@ motion):
172
173
 
173
174
  | Stock-looking class | What it resolves to |
174
175
  |---|---|
176
+ | `tier-icon/pill/chip/control/bar` | indivisible bounded height + concentric radius |
175
177
  | `rounded-icon/pill/chip/control/bar` | the concentric radius ladder (collapses on `data-corners="square"`) |
176
178
  | `rounded-menu-item` | menu-row radius, concentric inside a `rounded-control` panel |
177
179
  | `shadow` / `shadow-lg` | resting control edge / floating panel chrome, per surface |
@@ -197,10 +199,13 @@ The spacing names (`gutter`, `card`, `panel`, `content`, `menu`, `field`, the
197
199
  `inset-*` and concentric `*-inset-*` insets, `glyph-*`) are registered as a
198
200
  Tailwind spacing scale, so every prefix derives automatically: `p-`, `px-`,
199
201
  `pt-`, `mt-`, `gap-`, `gap-x-`, `inset-`, `translate-`, and the rest. The TIER
200
- names (`icon`, `pill`, `chip`, `control`, `bar`) are heights, not spacing, so
202
+ names (`icon`, `pill`, `chip`, `control`, `bar`) are dimensions, not spacing, so
201
203
  they are registered per prefix instead — `h-`, `w-`, `min-h-`, `min-w-` and
202
204
  `size-` — and the two vocabularies deliberately share no word. Putting a tier
203
205
  in the spacing scale would silently make `px-control` mean 24px of padding.
206
+ For chrome, use `tier-T`, which binds `h-T` and `rounded-T`; use the separate
207
+ dimension/radius names only for width, minimums, unbounded surfaces, or
208
+ asymmetric/docked corners.
204
209
 
205
210
  The var form (`h-(--era-h-control)`) still works and is the escape hatch for a
206
211
  token with no registered name — `--era-sp`, the press-sink offsets, anything
@@ -219,8 +224,9 @@ source uses it where a name exists.
219
224
  | `era-token-ink` | the ink centring for a token pill — a badge, a KV value, any short caps-or-digits label in a fixed-height box, CENTRED horizontally and with no icon sibling. For a left-aligned sentence-case label use era-label-ink; when an icon really must sit beside the text, see the note there. |
220
225
  | `era-label-ink` | the ink centring for a fixed-height control holding ONE line of sentence-case text and no icon — a menu item, a nav trigger, a button label. Never alongside `truncate` (the trimmed box becomes the clip). |
221
226
  | `glass-blur` | the backdrop blur for a floating panel (popover, dialog, taskbar) — it applies only inside a data-surface="glass" subtree and is inert elsewhere, so it is safe to leave on a panel that renders under every surface. |
222
- | `rounded-row` | the radius for a row inside a container that pads it by the gutter a nav rail, a command list, any scrolling list of controls. For a row in a menu PANEL, whose inset is the surface-owned frame rather than the gutter, use rounded-menu-item. |
227
+ | `tier-icon` `tier-pill` `tier-chip` `tier-control` `tier-bar` | — |
223
228
  | `h-line` | the height of one line of the element's own text — a skeleton standing in for a line, a spacer that must match a line box. |
229
+ | `era-track-well` | instead of `shadow-well` on a track whose value is painted by a child that covers the box — a progress/meter/slider track, a custom gauge. For a well holding ordinary content, `shadow-well` is still right. |
224
230
  | `era-skeleton-pulse` | — |
225
231
  | `era-shimmer` | the in-progress text treatment — put it on a "working…" / "thinking…" label while a task streams, instead of a spinner beside the text. |
226
232
  | `era-collapse` | the height sweep for a bits-ui Collapsible.Content — put it on the Content element itself (it carries its own overflow: hidden). |
@@ -241,12 +247,12 @@ source uses it where a name exists.
241
247
  | `bg-well` `text-well` `border-well` `bg-elevated` `text-elevated` `border-elevated` `bg-highlight` `text-highlight` `border-highlight` `bg-overlay` `text-overlay` `border-overlay` | — |
242
248
  | `p-gutter` `px-gutter` `py-gutter` `gap-gutter` `p-panel` `px-panel` `py-panel` `gap-panel` `p-card` `px-card` `py-card` `gap-card` `p-content` `px-content` `py-content` `gap-content` `p-menu` `px-menu` `py-menu` `gap-menu` `p-field` `px-field` `py-field` `gap-field` | — |
243
249
  | `p-inset-pill` `px-inset-pill` `py-inset-pill` `gap-inset-pill` `p-inset-chip` `px-inset-chip` `py-inset-chip` `gap-inset-chip` `p-inset-control` `px-inset-control` `py-inset-control` `gap-inset-control` | text-symmetric content padding — (tier − text)/2, the inset that vertically centres body text in its tier; px-inset-control is a text button's side padding, p-inset-control a code block's. |
244
- | `p-icon-inset-pill` `px-icon-inset-pill` `py-icon-inset-pill` `gap-icon-inset-pill` `p-icon-inset-chip` `px-icon-inset-chip` `py-icon-inset-chip` `gap-icon-inset-chip` `p-icon-inset-control` `px-icon-inset-control` `py-icon-inset-control` `gap-icon-inset-control` `p-icon-inset-bar` `px-icon-inset-bar` `py-icon-inset-bar` `gap-icon-inset-bar` `p-pill-inset-chip` `px-pill-inset-chip` `py-pill-inset-chip` `gap-pill-inset-chip` `p-pill-inset-control` `px-pill-inset-control` `py-pill-inset-control` `gap-pill-inset-control` `p-pill-inset-bar` `px-pill-inset-bar` `py-pill-inset-bar` `gap-pill-inset-bar` `p-chip-inset-control` `px-chip-inset-control` `py-chip-inset-control` `gap-chip-inset-control` `p-chip-inset-bar` `px-chip-inset-bar` `py-chip-inset-bar` `gap-chip-inset-bar` | the concentric inset for a nested pair — (h-container − h-child)/2, the gap that makes a child control sit evenly inside its container, and the radius step that keeps their corners concentric. Named for the pair it joins: gap-icon-inset-control seats an icon in a control. |
250
+ | `p-icon-inset-pill` `px-icon-inset-pill` `py-icon-inset-pill` `gap-icon-inset-pill` `p-icon-inset-chip` `px-icon-inset-chip` `py-icon-inset-chip` `gap-icon-inset-chip` `p-icon-inset-control` `px-icon-inset-control` `py-icon-inset-control` `gap-icon-inset-control` `p-icon-inset-bar` `px-icon-inset-bar` `py-icon-inset-bar` `gap-icon-inset-bar` `p-pill-inset-chip` `px-pill-inset-chip` `py-pill-inset-chip` `gap-pill-inset-chip` `p-pill-inset-control` `px-pill-inset-control` `py-pill-inset-control` `gap-pill-inset-control` `p-pill-inset-bar` `px-pill-inset-bar` `py-pill-inset-bar` `gap-pill-inset-bar` `p-chip-inset-control` `px-chip-inset-control` `py-chip-inset-control` `gap-chip-inset-control` `p-chip-inset-bar` `px-chip-inset-bar` `py-chip-inset-bar` `gap-chip-inset-bar` `p-control-inset-bar` `px-control-inset-bar` `py-control-inset-bar` `gap-control-inset-bar` | the concentric inset for a nested pair — (h-container − h-child)/2, the gap that makes a child control sit evenly inside its container, and the radius step that keeps their corners concentric. Named for the pair it joins: gap-icon-inset-control seats an icon in a control. |
245
251
  | `p-glyph-icon` `px-glyph-icon` `py-glyph-icon` `gap-glyph-icon` `p-glyph-pill` `px-glyph-pill` `py-glyph-pill` `gap-glyph-pill` | the glyph size for a tier — the INK inside the box, not the box (size-glyph-pill is the dismiss × in a pill, size-pill is the pill). |
246
252
  | `bg-field` `text-field` `border-field` `bg-field-highlight` `text-field-highlight` `border-field-highlight` | the recessed field fill — bg-field is a text input's resting well, bg-field-highlight its focused/hovered face. |
247
253
  | `ease-base` | — |
248
254
  | `bg-divider` `text-divider` `border-divider` `bg-divider-faded` `text-divider-faded` `border-divider-faded` `bg-background` `text-background` `border-background` `bg-foreground` `text-foreground` `border-foreground` `bg-1` `text-1` `border-1` `bg-2` `text-2` `border-2` `bg-3` `text-3` `border-3` `bg-4` `text-4` `border-4` `bg-5` `text-5` `border-5` `bg-6` `text-6` `border-6` `bg-7` `text-7` `border-7` `bg-8` `text-8` `border-8` `bg-9` `text-9` `border-9` `bg-10` `text-10` `border-10` `bg-11` `text-11` `border-11` `bg-12` `text-12` `border-12` `bg-border` `text-border` `border-border` `bg-surface` `text-surface` `border-surface` `bg-hover` `text-hover` `border-hover` `bg-fg` `text-fg` `border-fg` `bg-bright` `text-bright` `border-bright` `bg-muted` `text-muted` `border-muted` | — |
249
- | `bg-fill` `text-fill` `border-fill` `bg-link` `text-link` `border-link` `bg-primary` `text-primary` `border-primary` `bg-primary-fg` `text-primary-fg` `border-primary-fg` `bg-primary-hover` `text-primary-hover` `border-primary-hover` `bg-destructive` `text-destructive` `border-destructive` `bg-destructive-fg` `text-destructive-fg` `border-destructive-fg` `bg-success` `text-success` `border-success` `bg-success-fg` `text-success-fg` `border-success-fg` `bg-warning` `text-warning` `border-warning` `bg-warning-fg` `text-warning-fg` `border-warning-fg` | — |
255
+ | `bg-fill` `text-fill` `border-fill` `bg-link` `text-link` `border-link` `bg-primary` `text-primary` `border-primary` `bg-primary-fg` `text-primary-fg` `border-primary-fg` `bg-primary-hover` `text-primary-hover` `border-primary-hover` `bg-destructive` `text-destructive` `border-destructive` `bg-destructive-fg` `text-destructive-fg` `border-destructive-fg` `bg-success` `text-success` `border-success` `bg-success-fg` `text-success-fg` `border-success-fg` `bg-warning` `text-warning` `border-warning` `bg-warning-fg` `text-warning-fg` `border-warning-fg` `bg-info` `text-info` `border-info` `bg-info-fg` `text-info-fg` `border-info-fg` | — |
250
256
 
251
257
  ## era-interactive
252
258
 
@@ -628,35 +634,42 @@ Declared in `index.css`.
628
634
  }
629
635
  ```
630
636
 
631
- ## rounded-row
637
+ ## tier-icon · tier-pill · tier-chip · tier-control · tier-bar
632
638
 
633
- **Use it for:** the radius for a row inside a container that pads it by the gutter a nav rail, a command list, any scrolling list of controls. For a row in a menu PANEL, whose inset is the surface-owned frame rather than the gutter, use rounded-menu-item.
639
+ BOUNDED GEOMETRYheight and radius are one decision.
634
640
 
635
- THE ROW RADIUS concentricity for a row that is inset, WITH its inset intact.
641
+ A tier is a geometric type, not two classes a caller must remember to keep in
642
+ sync. `tier-control` establishes h-control AND rd-control; the radius ladder
643
+ then guarantees rd_outer − rd_inner = (h_outer − h_inner)/2 for every pair.
644
+ Width remains the component's decision: a row stretches, a button adds
645
+ min-w-control, and a square control adds w-control.
636
646
 
637
- The law is rd_outer − rd_inner = gap. A row inside a container that pads it by
638
- the gutter therefore takes the container's radius MINUS the gutter — and no
639
- tier radius equals that at more than one density, which is why reaching for
640
- `rounded-control` and hoping looks wrong: in a control-radius pane the row and
641
- the pane end up with identical curvature separated by 5px of gap, two arcs at
642
- one corner with no relationship.
647
+ Declared in `index.css`.
643
648
 
644
- The alternative — deleting the gap so equal radii become legal — is the wrong
645
- trade and was briefly shipped. The ladder exists so a gap and concentric
646
- corners COEXIST; spending one to buy the other gives up the thing it is for.
649
+ ```css
650
+ @utility tier-icon {
651
+ height: var(--era-h-icon);
652
+ border-radius: var(--era-rd-icon);
653
+ }
647
654
 
648
- The calc runs on the ROW, not on the container, which is what makes it
649
- automatic: `--era-container-rd` is inherited, so a container that is not
650
- control-radius publishes its own and every row inside re-derives. era's
651
- command palette does exactly that — it is rd-bar, so its rows resolve to
652
- rd-bar − gap, which IS rd-control (control-inset-bar is the gutter), and the
653
- palette's geometry is unchanged by this.
655
+ @utility tier-pill {
656
+ height: var(--era-h-pill);
657
+ border-radius: var(--era-rd-pill);
658
+ }
654
659
 
655
- Declared in `index.css`.
660
+ @utility tier-chip {
661
+ height: var(--era-h-chip);
662
+ border-radius: var(--era-rd-chip);
663
+ }
656
664
 
657
- ```css
658
- @utility rounded-row {
659
- border-radius: max(0px, calc(var(--era-container-rd, var(--era-rd-control)) - var(--era-gap)));
665
+ @utility tier-control {
666
+ height: var(--era-h-control);
667
+ border-radius: var(--era-rd-control);
668
+ }
669
+
670
+ @utility tier-bar {
671
+ height: var(--era-h-bar);
672
+ border-radius: var(--era-rd-bar);
660
673
  }
661
674
  ```
662
675
 
@@ -681,6 +694,50 @@ Declared in `index.css`.
681
694
  }
682
695
  ```
683
696
 
697
+ ## era-track-well
698
+
699
+ **Use it for:** instead of `shadow-well` on a track whose value is painted by a child that covers the box — a progress/meter/slider track, a custom gauge. For a well holding ordinary content, `shadow-well` is still right.
700
+
701
+ A WELL THAT SURVIVES BEING FILLED.
702
+
703
+ `shadow-well` is an INSET shadow, and an inset shadow belongs to the
704
+ element's background layer — it paints under the element's own descendants.
705
+ That is invisible on a well holding text (a checkbox, a code block: the
706
+ chrome runs around the content) and wrong the moment something paints the
707
+ whole box. Meter, Progress and the Slider track all do exactly that: an
708
+ opaque child, `translateX`-ed to the value. So the well survived only where
709
+ the track was EMPTY — a half-full meter on bevel had a chisel to the right
710
+ of the value and none to the left, which reads as a hole with a sticker
711
+ over it rather than a level inside a well.
712
+
713
+ The fix is painting order, not geometry: the same `--era-shadow-well` on a
714
+ pseudo-element that is the element's LAST child, so it lands above the fill.
715
+ The fill still spans the full box — nothing is inset, no radius is
716
+ re-derived, and 0% and 100% keep their exact ends. The `position: relative`
717
+ is load-bearing (the overlay resolves against this box) and `inherit` on the
718
+ radius keeps it honest under `data-corners="square"`.
719
+
720
+ Surface-agnostic on purpose. It reads the same token every well reads, so
721
+ glass's hairline rim is restored by the same line that restores bevel's
722
+ chisel, and flat — where the token is `none` — renders exactly as before.
723
+ Reported against bevel by a consumer; it was never a bevel bug.
724
+
725
+ Declared in `index.css`.
726
+
727
+ ```css
728
+ @utility era-track-well {
729
+ position: relative;
730
+ &::after {
731
+ content: '';
732
+ position: absolute;
733
+ inset: 0;
734
+ border-radius: inherit;
735
+ box-shadow: var(--era-shadow-well);
736
+ pointer-events: none;
737
+ }
738
+ }
739
+ ```
740
+
684
741
  ## era-skeleton-pulse
685
742
 
686
743
  Skeleton's pulse, on the MOTION AXIS.
@@ -1170,7 +1227,7 @@ Declared in `index.css`.
1170
1227
  }
1171
1228
  ```
1172
1229
 
1173
- ## p-icon-inset-pill · px-icon-inset-pill · py-icon-inset-pill · gap-icon-inset-pill · p-icon-inset-chip · px-icon-inset-chip · py-icon-inset-chip · gap-icon-inset-chip · … (36 in all)
1230
+ ## p-icon-inset-pill · px-icon-inset-pill · py-icon-inset-pill · gap-icon-inset-pill · p-icon-inset-chip · px-icon-inset-chip · py-icon-inset-chip · gap-icon-inset-chip · … (40 in all)
1174
1231
 
1175
1232
  **Use it for:** the concentric inset for a nested pair — (h-container − h-child)/2, the gap that makes a child control sit evenly inside its container, and the radius step that keeps their corners concentric. Named for the pair it joins: gap-icon-inset-control seats an icon in a control.
1176
1233
 
@@ -1187,6 +1244,7 @@ Declared in `index.css`.
1187
1244
  --spacing-pill-inset-bar: var(--era-pill-inset-bar);
1188
1245
  --spacing-chip-inset-control: var(--era-chip-inset-control);
1189
1246
  --spacing-chip-inset-bar: var(--era-chip-inset-bar);
1247
+ --spacing-control-inset-bar: var(--era-control-inset-bar);
1190
1248
  }
1191
1249
  ```
1192
1250
 
@@ -1265,7 +1323,7 @@ Declared in `index.css`.
1265
1323
  }
1266
1324
  ```
1267
1325
 
1268
- ## bg-fill · text-fill · border-fill · bg-link · text-link · border-link · bg-primary · text-primary · … (33 in all)
1326
+ ## bg-fill · text-fill · border-fill · bg-link · text-link · border-link · bg-primary · text-primary · … (39 in all)
1269
1327
 
1270
1328
  The standing pill fill — --era-fill, the latched counterpart of the
1271
1329
  transient highlight. Surface-owned: a composing wash on flat/bevel, the
@@ -1286,6 +1344,8 @@ Declared in `index.css`.
1286
1344
  --color-success-fg: var(--color-success-fg);
1287
1345
  --color-warning: var(--color-warning);
1288
1346
  --color-warning-fg: var(--color-warning-fg);
1347
+ --color-info: var(--color-info);
1348
+ --color-info-fg: var(--color-info-fg);
1289
1349
  }
1290
1350
  ```
1291
1351
 
@@ -1456,7 +1516,7 @@ Inherits all props from `HTMLAttributes<HTMLSpanElement>`.
1456
1516
  | Prop | Type | Default | Notes |
1457
1517
  |------|------|---------|-------|
1458
1518
  | `ref?` | `HTMLSpanElement \| null` | `null` | bindable |
1459
- | `tone?` | `'default' \| 'accent' \| 'destructive' \| 'success' \| 'warning'` | `'default'` | — |
1519
+ | `tone?` | `'default' \| 'accent' \| 'destructive' \| 'success' \| 'warning' \| 'info'` | `'default'` | — |
1460
1520
  | `lead?` | `Snippet` | — | Icon rendered BEFORE the label, outside it. Use this rather than putting
1461
1521
  an icon in `children`: the label then gets its own box, which is the only
1462
1522
  place era's ink centring can reach. |
@@ -1547,10 +1607,28 @@ Inherits all props from `HTMLAttributes<HTMLDivElement>`.
1547
1607
  |------|------|---------|-------|
1548
1608
  | `ref?` | `HTMLDivElement \| null` | `null` | bindable |
1549
1609
  | `size?` | `'chip' \| 'control' \| 'bar'` | `'bar'` | Height tier (scales with density). Default `lg` — a full toolbar. |
1550
- | `content?` | `'tier' \| 'pill'` | `'tier'` | Tier of the controls inside — drives the bar's padding and gap so they
1551
- stay concentric to them. `tier` (default) is one tier smaller; `pill` is
1552
- the compact pairing (pills/fields in an sm or md bar). |
1553
- | `divider?` | `boolean` | `false` | The flush cap-a-panel chrome: outset divider below, bottom rd-md. |
1610
+ | `content?` | `BarContent` | | Tier of the controls inside — drives the bar's padding and gap so they
1611
+ stay concentric to them. Omit this for the next canonical rung, or name
1612
+ the actual tier when the contents are smaller. |
1613
+ | `divider?` | `boolean` | `false` | The flush cap-a-panel chrome: an outset rule below, and a rounded
1614
+ BOTTOM only — the top defers to the panel's own radius.
1615
+ THE BOTTOM RADIUS IS LOAD-BEARING FOR THE RULE. The divider is an
1616
+ outset `box-shadow`, so it follows the box's corners: the rounded
1617
+ bottom is what curves the line up at the ends and makes the bar read
1618
+ as a long pill. Overriding the radius therefore changes the RULE, not
1619
+ just the corners. Measured against `cn`:
1620
+ - `rounded-none` drops both — a square bar with a dead-straight rule.
1621
+ That is the intended square mode (an in-app Notes bar passes it).
1622
+ - `rounded-b-*` or `rounded-b-none` replaces the curve — this is the
1623
+ one that surprises, because the rule changes shape when the caller
1624
+ thought they were only squaring a corner.
1625
+ - `rounded-t-*` is SAFE: it wins against `rounded-t-none` and leaves
1626
+ `rounded-b-control` standing, so the curve survives.
1627
+ A consumer lost the curve this way. The coupling was already written
1628
+ down — on the variant, inside `<script module>`, where someone
1629
+ hovering this prop never reaches it. Docs are only as good as the
1630
+ place the reader is standing. |
1631
+ | `topology?` | `'nested' \| 'docked'` | `'nested'` | `docked` removes one or more exposed curves; strict radius nesting does not apply. |
1554
1632
  | `children?` | `Snippet` | — | — |
1555
1633
 
1556
1634
  <!-- end: bar -->
@@ -1580,7 +1658,7 @@ Inherits all props from `HTMLAttributes<HTMLElement>`.
1580
1658
  |------|------|---------|-------|
1581
1659
  | `ref?` | `HTMLElement \| null` | `null` | bindable |
1582
1660
  | `size?` | `'chip' \| 'control' \| 'bar'` | `'bar'` | Height tier, exactly as Bar's — the geometry is Bar's, not a second copy. |
1583
- | `content?` | `'tier' \| 'pill'` | `'tier'` | Tier of the items inside, so the wall stays concentric to them. |
1661
+ | `content?` | `BarContent` | | Tier of the items inside, so the wall stays concentric to them. |
1584
1662
  | `divider?` | `boolean` | `false` | Draw Bar's flush cap-a-panel rule beneath the bar. Off by default,
1585
1663
  because a nav that sits in a page already has whatever separation the
1586
1664
  layout gives it — and a link-variant nav in particular reads as text,
@@ -1605,6 +1683,13 @@ Inherits all props from `HTMLAnchorAttributes`.
1605
1683
  drops the chrome entirely and renders running text, for a primary nav
1606
1684
  that should read as links rather than a row of raised keycaps. Existing
1607
1685
  bars are byte-identical. |
1686
+ | `currentCue?` | `'thickness' \| 'ink' \| 'none'` | `'thickness'` | WHAT MARKS THE CURRENT ITEM. `thickness` (default) is the three-state
1687
+ decoration — portable across every theme and font because it is one
1688
+ ink at three strengths. `ink` brightens the current item instead, and
1689
+ is theme-dependent by construction: era's default themes point
1690
+ --color-link at --color-bright, so its delta there is zero. `none`
1691
+ silences era's cue entirely, for a consumer painting their own.
1692
+ See navItemVariants above for the measurements behind each. |
1608
1693
  | `current?` | `boolean` | `false` | Whether this item points at the page you are on.
1609
1694
  There is deliberately no `active` prop beside this one. `active` would
1610
1695
  be a second source of truth for one fact, and the two drift the moment
@@ -1644,14 +1729,47 @@ Inherits all props from `Button.RootProps`.
1644
1729
 
1645
1730
  | Prop | Type | Default | Notes |
1646
1731
  |------|------|---------|-------|
1647
- | `tone?` | `'default' \| 'accent' \| 'destructive' \| 'success' \| 'warning'` | `'default'` | — |
1732
+ | `tone?` | `'default' \| 'accent' \| 'destructive' \| 'success' \| 'warning' \| 'info'` | `'default'` | — |
1648
1733
  | `size?` | `'control' \| 'chip' \| 'pill' \| 'icon'` | `'control'` | — |
1649
1734
  | `variant?` | `'filled' \| 'link'` | `'filled'` | — |
1650
1735
  | `active?` | `boolean` | `false` | — |
1651
1736
  | `icon?` | `boolean` | `false` | — |
1652
- | `loading?` | `boolean` | `false` | When true, disables the button, sets `aria-busy`, and prepends a
1653
- spinner. The existing label stays rendered so focus, click targets,
1654
- and screen-reader accessible names don't change shape mid-flight. |
1737
+ | `loading?` | `boolean` | `false` | In flight: disables the button, sets `aria-busy`, and puts a spinner
1738
+ over the label.
1739
+ THE GEOMETRY DOES NOT MOVE. That is the requirement, not a detail. A
1740
+ button is loading because it was just CLICKED, so anything `loading`
1741
+ changes about the box moves the page under the pointer that is still
1742
+ on it — and a spinner PREPENDED to the label (what this used to do)
1743
+ costs an icon's width plus a gap, measured on a wrapping header row
1744
+ that reflowed everything beside it. A consumer hit exactly that and
1745
+ stopped using the prop, hand-rolling `disabled` + `aria-busy`
1746
+ instead, which is the report that produced this note.
1747
+ So the spinner is absolutely positioned and takes no part in the
1748
+ flex layout, and the label goes to `opacity-0` rather than being
1749
+ unmounted: the box that holds the width stays, and the text stays in
1750
+ the accessibility tree as the button's accessible name.
1751
+ The visible label is therefore HIDDEN in flight — the one thing this
1752
+ trades away, and the reason `loadingLabel` exists.
1753
+ IF YOU DO NOT WANT A SPINNER, use `busy`. Not showing one is a
1754
+ supported posture, not a legacy one — see that prop. |
1755
+ | `busy?` | `boolean` | `false` | In flight, WITHOUT a spinner: disables the button and sets `aria-busy`,
1756
+ and changes nothing else. The label stays visible and legible.
1757
+ This exists because "show a spinner" is a taste, not a requirement,
1758
+ and era had only one answer. A consumer whose house style is that
1759
+ spinners should not appear had to spell the posture out themselves —
1760
+ `disabled` plus a restated `aria-busy` — and rely on knowing that
1761
+ `restProps` spreads last, which is a real fact about this component
1762
+ that no reader should have to depend on. Two words for two postures
1763
+ is clearer than one word plus a workaround.
1764
+ `loading` implies this: it is `busy` plus the spinner and the hidden
1765
+ label. What you get from `busy` alone is a control that is visibly
1766
+ disabled (the era-interactive opacity) and announced as busy, with
1767
+ its geometry and its ink untouched — which is also why it needs no
1768
+ `loadingLabel`: nothing about the button changed except that you
1769
+ cannot press it.
1770
+ Reported by a consumer whose user asked for no spinners anywhere.
1771
+ They were right that a fix for the width shift did not answer it: a
1772
+ spinner that no longer moves its neighbours is still a spinner. |
1655
1773
  | `loadingLabel?` | `string` | — | Screen-reader-only label announced while `loading` is true. |
1656
1774
  | `lead?` | `Snippet` | — | Icon rendered BEFORE the label, outside it. Use this rather than putting
1657
1775
  an icon in `children`: the label then gets its own box, which is the only
@@ -1689,6 +1807,7 @@ snippet's text cannot be read before it renders.
1689
1807
  Only reaches the label when `lead`/`trail` give it its own box. Without
1690
1808
  one the label is an anonymous flex item that no selector can address,
1691
1809
  which is the whole reason those snippets exist. |
1810
+ | `topology?` | `'bounded' \| 'docked'` | `'bounded'` | `docked` joins this control to a neighbour and removes one or more curves. |
1692
1811
  | `children?` | `Snippet` | — | — |
1693
1812
  | `ref?` | `(forwarded)` | `null` | bindable |
1694
1813
 
@@ -1799,11 +1918,41 @@ Inherits all props from `ButtonBits`.
1799
1918
  |------|------|---------|-------|
1800
1919
  | `ref?` | `CardElement \| null` | `null` | bindable |
1801
1920
  | `tone?` | `'elevated' \| 'surface' \| 'well' \| 'flat'` | `'elevated'` | Surface tier the card paints on.
1802
- - `elevated` (default): the lifted surface. Use on the page background.
1803
- - `surface`: the base surface tier. Use when nesting cards inside another elevated container.
1804
- - `well`: recessed. For content the surrounding UI puts INTO the card a tool's
1805
- output pane, a transcript body — the container twin of a text field's fill.
1806
- - `flat`: no background; only the rounded/padded shell. For grouping content visually without adding contrast. |
1921
+ - `elevated` (default): the lifted surface, with a resting shadow. Use
1922
+ on the page background.
1923
+ - `surface`: the base surface tier, no shadow. Use when nesting cards
1924
+ inside another elevated container.
1925
+ - `well`: recessed. For content the surrounding UI puts INTO the card
1926
+ a tool's output pane, a transcript body — the container twin of a
1927
+ text field's fill.
1928
+ - `flat`: no background; only the rounded/padded shell. For grouping
1929
+ content visually without adding contrast.
1930
+ READ THE CLASSES AND THEY LOOK SWAPPED. They are not, and this is
1931
+ worth a sentence because a consumer read them, believed it, and
1932
+ shipped flat cards for months. `surface` paints `bg-well` and `well`
1933
+ paints `bg-field`, because era's utility names describe FILLS rather
1934
+ than tiers: `bg-well` is the surface's own resting fill (it is what a
1935
+ panel is painted with, and it pairs with `shadow-well` when you want
1936
+ recessed chrome), and `bg-field` is the recessed fill a text input
1937
+ sits in. The tone names are the tiers; the class names are the paints.
1938
+ A CARD THAT IS PRESSABLE SHOULD USUALLY BE `elevated`. Passing `href`
1939
+ or `onclick` turns on the interactive face — hover, focus, press — but
1940
+ it deliberately does NOT override the tier you asked for, so a
1941
+ `surface` link card does not get the raised tier's separation and can
1942
+ read flatter than the static cards around it. That combination was
1943
+ reported as a bug and it is a choice: a tone that silently changed
1944
+ under an event handler would be a worse one.
1945
+ HOW THE TIERS DIFFER IS SURFACE-DEPENDENT, so do not go looking for a
1946
+ fill difference on every surface. `base` mixes the elevated fill 6%
1947
+ toward `--color-bright` and adds a resting shadow, so there the tiers
1948
+ differ in COLOUR. `bevel` sets `--era-surface-bg-elevated` to
1949
+ `--era-surface-bg` outright — elevation is a chisel, not a tone, which
1950
+ is bevel's whole premise — so `surface` and `elevated` paint the
1951
+ IDENTICAL fill there and differ only in relief. A consumer measured
1952
+ exactly that on 30.8.0 bevel: both tones `oklch(0.195 0 none)` in dark,
1953
+ `0.863` in light, one variable, side by side. `glass` splits the tiers
1954
+ its own way again. So the tier is real on every surface; the thing
1955
+ carrying it is not always the fill. |
1807
1956
  | `header?` | `Snippet` | — | Rendered above the body with a subtle divider. |
1808
1957
  | `footer?` | `Snippet` | — | Rendered below the body with a subtle divider. |
1809
1958
  | `children?` | `Snippet` | — | — |
@@ -1832,6 +1981,22 @@ Inherits all props from `HTMLAttributes<HTMLDivElement>`.
1832
1981
  | `size?` | `'chip' \| 'pill'` | `'chip'` | `sm` (default) is the standalone free chip; `pill` is the smaller pill
1833
1982
  that nests inside an md container (a select trigger, a button). |
1834
1983
  | `ondismiss?` | `() => void` | — | — |
1984
+ | `dismissAs?` | `'button' \| 'span'` | `'button'` | What the dismiss control RENDERS AS. `button` (the default) is right
1985
+ for a free-standing chip. Use `span` when the chip lives inside
1986
+ another control.
1987
+ A <button> inside a <button> is not merely bad manners: it is invalid,
1988
+ and the HTML parser repairs it by CLOSING the outer control before the
1989
+ inner one. So the server's markup and the client's DOM disagree, Svelte
1990
+ discards the page's hydration and re-renders from scratch, and the
1991
+ chips briefly sit outside the trigger they belong to. era's own
1992
+ multi-select shipped exactly that (a Chip with `ondismiss` inside
1993
+ Select.Trigger) and logged node_invalid_placement_ssr on every load.
1994
+ `span` also drops the control from the accessibility tree rather than
1995
+ announcing a button inside a button — which is the honest description
1996
+ of what it is there: a pointer shortcut. The accessible way to remove
1997
+ a selection is the listbox itself, which is unaffected. Keyboard focus
1998
+ order is unchanged, because a nested button was never reachable in a
1999
+ sensible order anyway. |
1835
2000
  | `lead?` | `Snippet` | — | Icon rendered BEFORE the label, outside it. Use this rather than putting
1836
2001
  an icon in `children`: the label then gets its own box, which is the only
1837
2002
  place era's ink centring can reach. |
@@ -1997,6 +2162,24 @@ Inherits all props from `Separator.RootProps`.
1997
2162
  | Prop | Type | Default | Notes |
1998
2163
  |------|------|---------|-------|
1999
2164
  | `variant?` | `'line' \| 'slant'` | `'line'` | Use `slant` for inline dividers in breadcrumbs, titles, and compact metadata. |
2165
+ | `size?` | `keyof typeof SIZES` | — | Give a VERTICAL rule a height of its own, from the tier ladder, instead
2166
+ of stretching to whatever line it lands in. Ignored when horizontal.
2167
+ WHY YOU WOULD WANT IT. A stretched rule is a passenger: it has no
2168
+ intrinsic height, so it measures whatever its neighbours measure and
2169
+ contributes nothing back. That is right inside a Bar, whose height is
2170
+ already fixed — and it is why a rule in an auto-height row cannot hold
2171
+ that row's height steady. A consumer whose site header gained and lost
2172
+ a qualifier between pages watched the chrome resize, and pinned a
2173
+ `min-h-[calc(...)]` on the header to stop it. Sizing the rule removes
2174
+ that calc: it becomes the piece that stands the row up.
2175
+ (For the record, since the report named the wrong mechanism: a
2176
+ stretched rule does not GROW its row. Measured in an 18px auto-height
2177
+ row, adding one leaves the row at 18px and the rule resolves to 10px —
2178
+ the line minus its two gutter margins. It can only ever be shorter
2179
+ than the row it divides, which is the whole problem here.)
2180
+ A sized rule drops the gutter margins with the stretch: the height is
2181
+ now a deliberate number, and silently adding 2 x gutter to it would
2182
+ make it the one element on the page you cannot reason about. |
2000
2183
  | `ref?` | `(forwarded)` | `null` | bindable |
2001
2184
 
2002
2185
  ## Usage
@@ -3910,12 +4093,90 @@ Inherits all props from `Meter.RootProps`.
3910
4093
 
3911
4094
  | Prop | Type | Default | Notes |
3912
4095
  |------|------|---------|-------|
3913
- | `tone?` | `'default' \| 'accent' \| 'destructive' \| 'success' \| 'warning'` | `'default'` | The band this reading falls in. Tone is the fill's colour only — the
4096
+ | `tone?` | `'default' \| 'accent' \| 'destructive' \| 'success' \| 'warning' \| 'info'` | `'default'` | The band this reading falls in. Tone is the fill's colour only — the
3914
4097
  track stays neutral, because the well belongs to the control and the
3915
4098
  fill belongs to the value.
3916
4099
  Tone carries no semantics on its own: a meter in the destructive band
3917
4100
  still needs an accessible label saying what has run out. Pair it with
3918
4101
  `aria-label` / a visible label, never rely on the hue alone. |
4102
+ | `threshold?` | `number` | — | A BOUNDARY INSIDE THE RANGE — in the same units as `value`, not a
4103
+ percentage. A quota with a soft limit before the hard one; a plan that
4104
+ includes 30 hours of the 50 it bills against; a temperature that is
4105
+ fine until it is not. The bar fills against `max`, and the mark says
4106
+ where the number the reader is actually deciding about sits.
4107
+ WHY THE MARK HAS NO COLOUR OF ITS OWN. It has to read against both
4108
+ sides of itself: over the empty track before the value reaches it, and
4109
+ over the fill afterwards. One token cannot do that — a colour tuned
4110
+ against the track disappears into the fill the moment the value
4111
+ crosses, which is exactly what a consumer hit hand-rolling this. So
4112
+ the mark is painted in the colour of the side it is NOT on: the fill's
4113
+ paint while the value is short of it, the track's fill once the value
4114
+ has passed. That contrast is not a new promise — it is the meter's
4115
+ own. A fill that did not stand out from its track would not be a
4116
+ meter, so borrowing the pair is the one choice that cannot be wrong in
4117
+ a theme, a tone, or a surface era has not seen.
4118
+ IT IS DECORATION, and silent on purpose. There is no `role` and
4119
+ nothing to announce: a screen reader gets `value`/`min`/`max` and the
4120
+ meter's own accessible name, and a boundary it cannot position adds
4121
+ nothing. If the threshold is part of the reading — it usually is —
4122
+ put the figure in `aria-label` and in a caption, where it is a number
4123
+ rather than a position.
4124
+ PASS `aria-valuetext` IF YOUR STORED UNIT IS NOT YOUR DISPLAYED ONE.
4125
+ `value`, `max` and `threshold` all have to sit in one unit for this
4126
+ arithmetic to work, and `aria-valuenow` is `value` verbatim — so a bar
4127
+ stored in minutes and read in hours announces "2640 of 3000", a figure
4128
+ the customer was never quoted. Converting the props is not available
4129
+ to you (it would move the mark), which makes `aria-valuetext` the only
4130
+ lever; it passes through `restProps` onto the same element. Reported
4131
+ by a consumer billing minutes and quoting hours.
4132
+ Outside `[min, max]` it renders NOTHING rather than clamping to an
4133
+ end, because a mark pinned to the edge of the bar would claim a
4134
+ boundary that is not in the range. |
4135
+ | `buffer?` | `number` | — | A REGION FROM `min` THAT IS A DIFFERENT KIND OF QUANTITY — painted
4136
+ under the fill, over the track, in the same units as `value`.
4137
+ The media player's buffered-progress shape, and the name is borrowed
4138
+ from it, but the reading is more general: some of the range is already
4139
+ accounted for. A plan whose first 30 of 50 hours are included in the
4140
+ monthly minimum is not "24 hours into a bill", it is "24 of 30 hours
4141
+ you have already paid for" — money spent against money that will be
4142
+ spent, which is a distinction the bar could not draw.
4143
+ NO COLOUR OF ITS OWN, for the reason `threshold` has none: it has to
4144
+ be legible over the track while never competing with the fill that
4145
+ crosses it, and a token tuned for one of those is wrong for the other.
4146
+ It is the fill's own paint at 30%, so it cannot be wrong in a theme,
4147
+ a tone or a surface era has not seen, and it is the same 30% Slider's
4148
+ `progress` has used for a seek bar's buffer since it shipped.
4149
+ UNDER the fill, so it disappears as the value consumes it. That is the
4150
+ reading, not a limitation: once you are past your included hours, the
4151
+ included region IS spent, and `threshold` is what goes on marking
4152
+ where it ended.
4153
+ CLAMPED to the range rather than dropped, which is where it differs
4154
+ from `threshold`. A region can be honestly truncated — a buffer past
4155
+ `max` means the whole visible bar is accounted for, and painting it
4156
+ all says exactly that. A point cannot: a mark pinned to the edge would
4157
+ claim a boundary that is not in the range, so an out-of-range
4158
+ `threshold` renders nothing instead.
4159
+ Silent, like the mark. `aria-valuetext` already carries the reading,
4160
+ and a second region a screen reader cannot position adds nothing.
4161
+ (Slider spells its equivalent `progress` and takes a 0–1 FRACTION.
4162
+ Here every positional prop is in the value's own units — `value`,
4163
+ `max`, `threshold`, `buffer` — because a component that mixed the two
4164
+ would be a bug generator. The names differ because on a Meter
4165
+ `progress` would be a synonym for `value`.) |
4166
+ | `start?` | `Snippet` | — | Captions under the two ends of the bar — what the range MEANS.
4167
+ A meter states a proportion and leaves the reader to work out what of.
4168
+ "0m used" on the left and "50h before approval" on the right is not
4169
+ decoration, it is the reading; every consumer of a bare meter ends up
4170
+ hand-rolling the same flex row underneath it, which is where this came
4171
+ from. `start` is left, `end` is right, either may stand alone.
4172
+ NOT ANNOUNCED. They are captions, so a screen reader gets the meter's
4173
+ value and its own `aria-label`, not these. If the range needs saying
4174
+ out loud, say it in the label too — the same rule `tone` carries.
4175
+ THE DOM IS UNCHANGED WITHOUT THEM. With neither snippet this renders
4176
+ exactly the element it always did, so `class` still lands on the track
4177
+ and the geometry audits still walk the same box. Passing one wraps the
4178
+ track in a column, and `class` still lands on the track. |
4179
+ | `end?` | `Snippet` | — | — |
3919
4180
  | `ref?` | `(forwarded)` | `null` | bindable |
3920
4181
 
3921
4182
  <!-- end: meter -->
@@ -102,8 +102,9 @@ Classes, not components — they need no import beyond the stylesheet. Reach for
102
102
  - `era-token-ink` — the ink centring for a token pill — a badge, a KV value, any short caps-or-digits label in a fixed-height box, CENTRED horizontally and with no icon sibling. For a left-aligned sentence-case label use era-label-ink; when an icon really must sit beside the text, see the note there.
103
103
  - `era-label-ink` — the ink centring for a fixed-height control holding ONE line of sentence-case text and no icon — a menu item, a nav trigger, a button label. Never alongside `truncate` (the trimmed box becomes the clip).
104
104
  - `glass-blur` — the backdrop blur for a floating panel (popover, dialog, taskbar) — it applies only inside a data-surface="glass" subtree and is inert elsewhere, so it is safe to leave on a panel that renders under every surface.
105
- - `rounded-row` the radius for a row inside a container that pads it by the gutter a nav rail, a command list, any scrolling list of controls. For a row in a menu PANEL, whose inset is the surface-owned frame rather than the gutter, use rounded-menu-item.
105
+ - `tier-icon · tier-pill · tier-chip · tier-control · tier-bar` BOUNDED GEOMETRYheight and radius are one decision.
106
106
  - `h-line` — the height of one line of the element's own text — a skeleton standing in for a line, a spacer that must match a line box.
107
+ - `era-track-well` — instead of `shadow-well` on a track whose value is painted by a child that covers the box — a progress/meter/slider track, a custom gauge. For a well holding ordinary content, `shadow-well` is still right.
107
108
  - `era-skeleton-pulse` — Skeleton's pulse, on the MOTION AXIS.
108
109
  - `era-shimmer` — the in-progress text treatment — put it on a "working…" / "thinking…" label while a task streams, instead of a spinner beside the text.
109
110
  - `era-collapse` — the height sweep for a bits-ui Collapsible.Content — put it on the Content element itself (it carries its own overflow: hidden).
@@ -124,11 +125,11 @@ Classes, not components — they need no import beyond the stylesheet. Reach for
124
125
  - `bg-well · text-well · border-well · bg-elevated · text-elevated · border-elevated · bg-highlight · text-highlight · … (12 in all)` — Surface fills — bg-well is the surface's own resting fill (pairs with
125
126
  - `p-gutter · px-gutter · py-gutter · gap-gutter · p-panel · px-panel · py-panel · gap-panel · … (24 in all)` — THE SPACING SCALE — one entry per token, from which Tailwind derives the
126
127
  - `p-inset-pill · px-inset-pill · py-inset-pill · gap-inset-pill · p-inset-chip · px-inset-chip · py-inset-chip · gap-inset-chip · … (12 in all)` — text-symmetric content padding — (tier − text)/2, the inset that vertically centres body text in its tier; px-inset-control is a text button's side padding, p-inset-control a code block's.
127
- - `p-icon-inset-pill · px-icon-inset-pill · py-icon-inset-pill · gap-icon-inset-pill · p-icon-inset-chip · px-icon-inset-chip · py-icon-inset-chip · gap-icon-inset-chip · … (36 in all)` — the concentric inset for a nested pair — (h-container − h-child)/2, the gap that makes a child control sit evenly inside its container, and the radius step that keeps their corners concentric. Named for the pair it joins: gap-icon-inset-control seats an icon in a control.
128
+ - `p-icon-inset-pill · px-icon-inset-pill · py-icon-inset-pill · gap-icon-inset-pill · p-icon-inset-chip · px-icon-inset-chip · py-icon-inset-chip · gap-icon-inset-chip · … (40 in all)` — the concentric inset for a nested pair — (h-container − h-child)/2, the gap that makes a child control sit evenly inside its container, and the radius step that keeps their corners concentric. Named for the pair it joins: gap-icon-inset-control seats an icon in a control.
128
129
  - `p-glyph-icon · px-glyph-icon · py-glyph-icon · gap-glyph-icon · p-glyph-pill · px-glyph-pill · py-glyph-pill · gap-glyph-pill` — the glyph size for a tier — the INK inside the box, not the box (size-glyph-pill is the dismiss × in a pill, size-pill is the pill).
129
130
  - `bg-field · text-field · border-field · bg-field-highlight · text-field-highlight · border-field-highlight` — the recessed field fill — bg-field is a text input's resting well, bg-field-highlight its focused/hovered face.
130
131
  - `ease-base` — Motion — bare transition utilities default to the motion axis, so
131
132
  - `bg-divider · text-divider · border-divider · bg-divider-faded · text-divider-faded · border-divider-faded · bg-background · text-background · … (66 in all)` — Surface divider colors: border-divider, bg-divider-faded, …
132
- - `bg-fill · text-fill · border-fill · bg-link · text-link · border-link · bg-primary · text-primary · … (33 in all)` — The standing pill fill — --era-fill, the latched counterpart of the
133
+ - `bg-fill · text-fill · border-fill · bg-link · text-link · border-link · bg-primary · text-primary · … (39 in all)` — The standing pill fill — --era-fill, the latched counterpart of the
133
134
 
134
135
  Full reference (what each emits, when to use it): `{{ORIGIN}}/utilities.md`. Machine-readable list: `{{ORIGIN}}/utilities.json`.