@signal9/era-ui 30.7.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 (87) hide show
  1. package/CHANGELOG.md +20 -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/alert-dialog/alert-dialog-action.svelte +2 -0
  30. package/dist/ui/alert-dialog/alert-dialog-cancel.svelte +2 -0
  31. package/dist/ui/badge/badge.svelte +8 -2
  32. package/dist/ui/badge/badge.svelte.d.ts +1 -1
  33. package/dist/ui/bar/bar.svelte +95 -26
  34. package/dist/ui/bar/bar.svelte.d.ts +71 -31
  35. package/dist/ui/button/button.svelte +38 -2
  36. package/dist/ui/button/button.svelte.d.ts +29 -0
  37. package/dist/ui/button/index.d.ts +2 -0
  38. package/dist/ui/button/variants.js +4 -9
  39. package/dist/ui/calendar/calendar-day.svelte +1 -1
  40. package/dist/ui/card/card.svelte +22 -9
  41. package/dist/ui/card/card.svelte.d.ts +19 -7
  42. package/dist/ui/chip/chip.svelte +5 -2
  43. package/dist/ui/chip/chip.svelte.d.ts +6 -6
  44. package/dist/ui/collapsible/collapsible-trigger.svelte +1 -1
  45. package/dist/ui/combobox/combobox-trigger.svelte +1 -1
  46. package/dist/ui/command/command-item.svelte +1 -1
  47. package/dist/ui/command/command-link-item.svelte +1 -1
  48. package/dist/ui/command/command-root.svelte +3 -5
  49. package/dist/ui/date-field/date-field-segment.svelte +1 -1
  50. package/dist/ui/date-picker/date-picker-day.svelte +1 -1
  51. package/dist/ui/date-picker/date-picker-segment.svelte +1 -1
  52. package/dist/ui/date-picker/date-picker-trigger.svelte +1 -1
  53. package/dist/ui/date-range-field/date-range-field-segment.svelte +1 -1
  54. package/dist/ui/date-range-picker/date-range-picker-day.svelte +1 -1
  55. package/dist/ui/date-range-picker/date-range-picker-segment.svelte +1 -1
  56. package/dist/ui/date-range-picker/date-range-picker-trigger.svelte +1 -1
  57. package/dist/ui/dialog/dialog-close.svelte +2 -0
  58. package/dist/ui/dialog/dialog-content.svelte +19 -1
  59. package/dist/ui/dropdown-menu/dropdown-menu-trigger.svelte +1 -1
  60. package/dist/ui/file-upload/file-upload.svelte +1 -1
  61. package/dist/ui/input/variants.d.ts +6 -6
  62. package/dist/ui/input/variants.js +2 -2
  63. package/dist/ui/kv/kv.svelte +1 -1
  64. package/dist/ui/menubar/menubar-root.svelte +1 -1
  65. package/dist/ui/menubar/menubar-trigger.svelte +1 -1
  66. package/dist/ui/nav/nav-bar.svelte +9 -4
  67. package/dist/ui/nav/nav-bar.svelte.d.ts +2 -1
  68. package/dist/ui/pane/pane-root.svelte +3 -12
  69. package/dist/ui/pane/pane-tab.svelte +1 -1
  70. package/dist/ui/select/select-trigger.svelte +1 -1
  71. package/dist/ui/slider/slider-thumb.svelte +1 -1
  72. package/dist/ui/step/step-summary.svelte +1 -1
  73. package/dist/ui/switch/switch.svelte +2 -2
  74. package/dist/ui/time-field/time-field-segment.svelte +1 -1
  75. package/dist/ui/time-range-field/time-range-field-segment.svelte +1 -1
  76. package/dist/ui/timeline/timeline-step.svelte +1 -1
  77. package/dist/ui/toggle-group/toggle-group-item.svelte +2 -2
  78. package/dist/ui/toggle-group/toggle-group-item.svelte.d.ts +6 -6
  79. package/dist/ui/toggle-group/toggle-group-root.svelte +1 -2
  80. package/dist/ui/toggle-group/toggle-group-root.svelte.d.ts +3 -3
  81. package/dist/ui/tooltip/tooltip-content.svelte +1 -1
  82. package/dist/ui/tree/tree-item.svelte +1 -1
  83. package/dist/ui/video-player/video-player.svelte +1 -1
  84. package/dist/utils/tw-merge-config.d.ts +3 -0
  85. package/dist/utils/tw-merge-config.js +4 -0
  86. package/package.json +4 -3
  87. package/skill/SKILL.md +17 -17
@@ -52,7 +52,7 @@
52
52
  "slug": "utilities",
53
53
  "title": "Utilities",
54
54
  "summary": "The CSS classes era ships alongside the components — links, ink-centred text, shimmer, hidden scrollbars.",
55
- "tokenEstimate": 13166,
55
+ "tokenEstimate": 13623,
56
56
  "sections": [
57
57
  "Overview",
58
58
  "era-interactive",
@@ -65,8 +65,9 @@
65
65
  "era-token-ink",
66
66
  "era-label-ink",
67
67
  "glass-blur",
68
- "rounded-row",
68
+ "tier-icon · tier-pill · tier-chip · tier-control · tier-bar",
69
69
  "h-line",
70
+ "era-track-well",
70
71
  "era-skeleton-pulse",
71
72
  "era-shimmer",
72
73
  "era-collapse",
@@ -87,12 +88,12 @@
87
88
  "bg-well · text-well · border-well · bg-elevated · text-elevated · border-elevated · bg-highlight · text-highlight · … (12 in all)",
88
89
  "p-gutter · px-gutter · py-gutter · gap-gutter · p-panel · px-panel · py-panel · gap-panel · … (24 in all)",
89
90
  "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)",
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 · … (36 in all)",
91
+ "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)",
91
92
  "p-glyph-icon · px-glyph-icon · py-glyph-icon · gap-glyph-icon · p-glyph-pill · px-glyph-pill · py-glyph-pill · gap-glyph-pill",
92
93
  "bg-field · text-field · border-field · bg-field-highlight · text-field-highlight · border-field-highlight",
93
94
  "ease-base",
94
95
  "bg-divider · text-divider · border-divider · bg-divider-faded · text-divider-faded · border-divider-faded · bg-background · text-background · … (66 in all)",
95
- "bg-fill · text-fill · border-fill · bg-link · text-link · border-link · bg-primary · text-primary · … (33 in all)"
96
+ "bg-fill · text-fill · border-fill · bg-link · text-link · border-link · bg-primary · text-primary · … (39 in all)"
96
97
  ],
97
98
  "file": "utilities.md"
98
99
  },
@@ -158,7 +159,7 @@
158
159
  "slug": "badge",
159
160
  "title": "Badge",
160
161
  "summary": "A small label for status or categorization.",
161
- "tokenEstimate": 279,
162
+ "tokenEstimate": 282,
162
163
  "sections": [
163
164
  "Import",
164
165
  "Props",
@@ -184,7 +185,7 @@
184
185
  "slug": "bar",
185
186
  "title": "Bar",
186
187
  "summary": "A flex container for toolbars and headers, in three heights.",
187
- "tokenEstimate": 211,
188
+ "tokenEstimate": 500,
188
189
  "sections": [
189
190
  "Import",
190
191
  "Props"
@@ -195,7 +196,7 @@
195
196
  "slug": "nav",
196
197
  "title": "Nav",
197
198
  "summary": "A navigation landmark and its items — the current one announces itself, and that announcement is what paints it.",
198
- "tokenEstimate": 825,
199
+ "tokenEstimate": 955,
199
200
  "sections": [
200
201
  "Import",
201
202
  "Nav.Bar",
@@ -207,7 +208,7 @@
207
208
  "slug": "button",
208
209
  "title": "Button",
209
210
  "summary": "A flexible button or link element.",
210
- "tokenEstimate": 1070,
211
+ "tokenEstimate": 1625,
211
212
  "sections": [
212
213
  "Import",
213
214
  "Props",
@@ -242,7 +243,7 @@
242
243
  "slug": "card",
243
244
  "title": "Card",
244
245
  "summary": "A surface container that can behave as a link or button.",
245
- "tokenEstimate": 285,
246
+ "tokenEstimate": 725,
246
247
  "sections": [
247
248
  "Import",
248
249
  "Props"
@@ -253,7 +254,7 @@
253
254
  "slug": "chip",
254
255
  "title": "Chip",
255
256
  "summary": "An interactive, optionally dismissible token.",
256
- "tokenEstimate": 329,
257
+ "tokenEstimate": 593,
257
258
  "sections": [
258
259
  "Import",
259
260
  "Props",
@@ -310,7 +311,7 @@
310
311
  "slug": "separator",
311
312
  "title": "Separator",
312
313
  "summary": "A visual divider between content.",
313
- "tokenEstimate": 218,
314
+ "tokenEstimate": 531,
314
315
  "sections": [
315
316
  "Import",
316
317
  "Props",
@@ -752,7 +753,7 @@
752
753
  "slug": "meter",
753
754
  "title": "Meter",
754
755
  "summary": "Displays a value within a known range.",
755
- "tokenEstimate": 188,
756
+ "tokenEstimate": 1469,
756
757
  "sections": [
757
758
  "Import",
758
759
  "Props"
@@ -1129,14 +1130,17 @@
1129
1130
  "css": "@utility` cannot be nested inside one. Surface-scoped RULES stay\n * in their surface file; surface-scoped UTILITIES have to come out.\n *\n * @use the backdrop blur for a floating panel (popover, dialog, taskbar) — it\n * applies only inside a data-surface=\"glass\" subtree and is inert elsewhere,\n * so it is safe to leave on a panel that renders under every surface. */\n@utility glass-blur {\n\t[data-surface='glass'] & {\n\t\tbackdrop-filter: blur(var(--era-blur)) saturate(var(--era-saturate));\n\t\t-webkit-backdrop-filter: blur(var(--era-blur)) saturate(var(--era-saturate));\n\t}\n}"
1130
1131
  },
1131
1132
  {
1132
- "name": "rounded-row",
1133
+ "name": "tier-icon",
1133
1134
  "names": [
1134
- "rounded-row"
1135
+ "tier-icon",
1136
+ "tier-pill",
1137
+ "tier-chip",
1138
+ "tier-control",
1139
+ "tier-bar"
1135
1140
  ],
1136
1141
  "file": "index.css",
1137
- "useCase": "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.",
1138
- "description": "THE ROW RADIUS — concentricity for a row that is inset, WITH its inset intact.\n\nThe law is rd_outer − rd_inner = gap. A row inside a container that pads it by\nthe gutter therefore takes the container's radius MINUS the gutter — and no\ntier radius equals that at more than one density, which is why reaching for\n`rounded-control` and hoping looks wrong: in a control-radius pane the row and\nthe pane end up with identical curvature separated by 5px of gap, two arcs at\none corner with no relationship.\n\nThe alternative — deleting the gap so equal radii become legal — is the wrong\ntrade and was briefly shipped. The ladder exists so a gap and concentric\ncorners COEXIST; spending one to buy the other gives up the thing it is for.\n\nThe calc runs on the ROW, not on the container, which is what makes it\nautomatic: `--era-container-rd` is inherited, so a container that is not\ncontrol-radius publishes its own and every row inside re-derives. era's\ncommand palette does exactly that — it is rd-bar, so its rows resolve to\nrd-bar − gap, which IS rd-control (control-inset-bar is the gutter), and the\npalette's geometry is unchanged by this.",
1139
- "css": "@utility rounded-row {\n\tborder-radius: max(0px, calc(var(--era-container-rd, var(--era-rd-control)) - var(--era-gap)));\n}"
1142
+ "description": "BOUNDED GEOMETRY — height and radius are one decision.\n\nA tier is a geometric type, not two classes a caller must remember to keep in\nsync. `tier-control` establishes h-control AND rd-control; the radius ladder\nthen guarantees rd_outer rd_inner = (h_outer h_inner)/2 for every pair.\nWidth remains the component's decision: a row stretches, a button adds\nmin-w-control, and a square control adds w-control.",
1143
+ "css": "@utility tier-icon {\n\theight: var(--era-h-icon);\n\tborder-radius: var(--era-rd-icon);\n}\n\n@utility tier-pill {\n\theight: var(--era-h-pill);\n\tborder-radius: var(--era-rd-pill);\n}\n\n@utility tier-chip {\n\theight: var(--era-h-chip);\n\tborder-radius: var(--era-rd-chip);\n}\n\n@utility tier-control {\n\theight: var(--era-h-control);\n\tborder-radius: var(--era-rd-control);\n}\n\n@utility tier-bar {\n\theight: var(--era-h-bar);\n\tborder-radius: var(--era-rd-bar);\n}"
1140
1144
  },
1141
1145
  {
1142
1146
  "name": "h-line",
@@ -1148,6 +1152,16 @@
1148
1152
  "description": "One LINE BOX tall — the height of the text this stands in for.\n\nSkeleton's `line` used h-(--era-text), which is the body FONT SIZE: never the\nheight of a rendered line (that is font-size x leading), and wrong again by\nthe ladder's ratio for any rung other than body. `1lh` resolves against the\nelement's OWN computed line-height, so putting a text-* class on the skeleton\nmakes it match that tier exactly, at every density, and it follows a leading\nchange for free.",
1149
1153
  "css": "@utility h-line {\n\theight: 1lh;\n}"
1150
1154
  },
1155
+ {
1156
+ "name": "era-track-well",
1157
+ "names": [
1158
+ "era-track-well"
1159
+ ],
1160
+ "file": "index.css",
1161
+ "useCase": "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.",
1162
+ "description": "A WELL THAT SURVIVES BEING FILLED.\n\n`shadow-well` is an INSET shadow, and an inset shadow belongs to the\nelement's background layer — it paints under the element's own descendants.\nThat is invisible on a well holding text (a checkbox, a code block: the\nchrome runs around the content) and wrong the moment something paints the\nwhole box. Meter, Progress and the Slider track all do exactly that: an\nopaque child, `translateX`-ed to the value. So the well survived only where\nthe track was EMPTY — a half-full meter on bevel had a chisel to the right\nof the value and none to the left, which reads as a hole with a sticker\nover it rather than a level inside a well.\n\nThe fix is painting order, not geometry: the same `--era-shadow-well` on a\npseudo-element that is the element's LAST child, so it lands above the fill.\nThe fill still spans the full box — nothing is inset, no radius is\nre-derived, and 0% and 100% keep their exact ends. The `position: relative`\nis load-bearing (the overlay resolves against this box) and `inherit` on the\nradius keeps it honest under `data-corners=\"square\"`.\n\nSurface-agnostic on purpose. It reads the same token every well reads, so\nglass's hairline rim is restored by the same line that restores bevel's\nchisel, and flat — where the token is `none` — renders exactly as before.\nReported against bevel by a consumer; it was never a bevel bug.",
1163
+ "css": "@utility era-track-well {\n\tposition: relative;\n\t&::after {\n\t\tcontent: '';\n\t\tposition: absolute;\n\t\tinset: 0;\n\t\tborder-radius: inherit;\n\t\tbox-shadow: var(--era-shadow-well);\n\t\tpointer-events: none;\n\t}\n}"
1164
+ },
1151
1165
  {
1152
1166
  "name": "era-skeleton-pulse",
1153
1167
  "names": [
@@ -1461,12 +1475,16 @@
1461
1475
  "p-chip-inset-bar",
1462
1476
  "px-chip-inset-bar",
1463
1477
  "py-chip-inset-bar",
1464
- "gap-chip-inset-bar"
1478
+ "gap-chip-inset-bar",
1479
+ "p-control-inset-bar",
1480
+ "px-control-inset-bar",
1481
+ "py-control-inset-bar",
1482
+ "gap-control-inset-bar"
1465
1483
  ],
1466
1484
  "file": "index.css",
1467
1485
  "useCase": "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.",
1468
1486
  "description": "",
1469
- "css": "@theme inline {\n\t--spacing-icon-inset-pill: var(--era-icon-inset-pill);\n\t--spacing-icon-inset-chip: var(--era-icon-inset-chip);\n\t--spacing-icon-inset-control: var(--era-icon-inset-control);\n\t--spacing-icon-inset-bar: var(--era-icon-inset-bar);\n\t--spacing-pill-inset-chip: var(--era-pill-inset-chip);\n\t--spacing-pill-inset-control: var(--era-pill-inset-control);\n\t--spacing-pill-inset-bar: var(--era-pill-inset-bar);\n\t--spacing-chip-inset-control: var(--era-chip-inset-control);\n\t--spacing-chip-inset-bar: var(--era-chip-inset-bar);\n}"
1487
+ "css": "@theme inline {\n\t--spacing-icon-inset-pill: var(--era-icon-inset-pill);\n\t--spacing-icon-inset-chip: var(--era-icon-inset-chip);\n\t--spacing-icon-inset-control: var(--era-icon-inset-control);\n\t--spacing-icon-inset-bar: var(--era-icon-inset-bar);\n\t--spacing-pill-inset-chip: var(--era-pill-inset-chip);\n\t--spacing-pill-inset-control: var(--era-pill-inset-control);\n\t--spacing-pill-inset-bar: var(--era-pill-inset-bar);\n\t--spacing-chip-inset-control: var(--era-chip-inset-control);\n\t--spacing-chip-inset-bar: var(--era-chip-inset-bar);\n\t--spacing-control-inset-bar: var(--era-control-inset-bar);\n}"
1470
1488
  },
1471
1489
  {
1472
1490
  "name": "p-glyph-icon",
@@ -1618,11 +1636,17 @@
1618
1636
  "border-warning",
1619
1637
  "bg-warning-fg",
1620
1638
  "text-warning-fg",
1621
- "border-warning-fg"
1639
+ "border-warning-fg",
1640
+ "bg-info",
1641
+ "text-info",
1642
+ "border-info",
1643
+ "bg-info-fg",
1644
+ "text-info-fg",
1645
+ "border-info-fg"
1622
1646
  ],
1623
1647
  "file": "index.css",
1624
1648
  "description": "The standing pill fill — --era-fill, the latched counterpart of the\ntransient highlight. Surface-owned: a composing wash on flat/bevel, the\nopaque swatch on glass.",
1625
- "css": "@theme inline {\n\t--color-fill: var(--era-fill);\n\t--color-link: var(--color-link);\n\t--color-primary: var(--color-primary);\n\t--color-primary-fg: var(--color-primary-fg);\n\t--color-primary-hover: var(--color-primary-hover);\n\t--color-destructive: var(--color-destructive);\n\t--color-destructive-fg: var(--color-destructive-fg);\n\t--color-success: var(--color-success);\n\t--color-success-fg: var(--color-success-fg);\n\t--color-warning: var(--color-warning);\n\t--color-warning-fg: var(--color-warning-fg);\n}"
1649
+ "css": "@theme inline {\n\t--color-fill: var(--era-fill);\n\t--color-link: var(--color-link);\n\t--color-primary: var(--color-primary);\n\t--color-primary-fg: var(--color-primary-fg);\n\t--color-primary-hover: var(--color-primary-hover);\n\t--color-destructive: var(--color-destructive);\n\t--color-destructive-fg: var(--color-destructive-fg);\n\t--color-success: var(--color-success);\n\t--color-success-fg: var(--color-success-fg);\n\t--color-warning: var(--color-warning);\n\t--color-warning-fg: var(--color-warning-fg);\n\t--color-info: var(--color-info);\n\t--color-info-fg: var(--color-info-fg);\n}"
1626
1650
  }
1627
1651
  ]
1628
1652
  }
@@ -14,10 +14,88 @@ Inherits all props from `Meter.RootProps`.
14
14
 
15
15
  | Prop | Type | Default | Notes |
16
16
  |------|------|---------|-------|
17
- | `tone?` | `'default' \| 'accent' \| 'destructive' \| 'success' \| 'warning'` | `'default'` | The band this reading falls in. Tone is the fill's colour only — the
17
+ | `tone?` | `'default' \| 'accent' \| 'destructive' \| 'success' \| 'warning' \| 'info'` | `'default'` | The band this reading falls in. Tone is the fill's colour only — the
18
18
  track stays neutral, because the well belongs to the control and the
19
19
  fill belongs to the value.
20
20
  Tone carries no semantics on its own: a meter in the destructive band
21
21
  still needs an accessible label saying what has run out. Pair it with
22
22
  `aria-label` / a visible label, never rely on the hue alone. |
23
+ | `threshold?` | `number` | — | A BOUNDARY INSIDE THE RANGE — in the same units as `value`, not a
24
+ percentage. A quota with a soft limit before the hard one; a plan that
25
+ includes 30 hours of the 50 it bills against; a temperature that is
26
+ fine until it is not. The bar fills against `max`, and the mark says
27
+ where the number the reader is actually deciding about sits.
28
+ WHY THE MARK HAS NO COLOUR OF ITS OWN. It has to read against both
29
+ sides of itself: over the empty track before the value reaches it, and
30
+ over the fill afterwards. One token cannot do that — a colour tuned
31
+ against the track disappears into the fill the moment the value
32
+ crosses, which is exactly what a consumer hit hand-rolling this. So
33
+ the mark is painted in the colour of the side it is NOT on: the fill's
34
+ paint while the value is short of it, the track's fill once the value
35
+ has passed. That contrast is not a new promise — it is the meter's
36
+ own. A fill that did not stand out from its track would not be a
37
+ meter, so borrowing the pair is the one choice that cannot be wrong in
38
+ a theme, a tone, or a surface era has not seen.
39
+ IT IS DECORATION, and silent on purpose. There is no `role` and
40
+ nothing to announce: a screen reader gets `value`/`min`/`max` and the
41
+ meter's own accessible name, and a boundary it cannot position adds
42
+ nothing. If the threshold is part of the reading — it usually is —
43
+ put the figure in `aria-label` and in a caption, where it is a number
44
+ rather than a position.
45
+ PASS `aria-valuetext` IF YOUR STORED UNIT IS NOT YOUR DISPLAYED ONE.
46
+ `value`, `max` and `threshold` all have to sit in one unit for this
47
+ arithmetic to work, and `aria-valuenow` is `value` verbatim — so a bar
48
+ stored in minutes and read in hours announces "2640 of 3000", a figure
49
+ the customer was never quoted. Converting the props is not available
50
+ to you (it would move the mark), which makes `aria-valuetext` the only
51
+ lever; it passes through `restProps` onto the same element. Reported
52
+ by a consumer billing minutes and quoting hours.
53
+ Outside `[min, max]` it renders NOTHING rather than clamping to an
54
+ end, because a mark pinned to the edge of the bar would claim a
55
+ boundary that is not in the range. |
56
+ | `buffer?` | `number` | — | A REGION FROM `min` THAT IS A DIFFERENT KIND OF QUANTITY — painted
57
+ under the fill, over the track, in the same units as `value`.
58
+ The media player's buffered-progress shape, and the name is borrowed
59
+ from it, but the reading is more general: some of the range is already
60
+ accounted for. A plan whose first 30 of 50 hours are included in the
61
+ monthly minimum is not "24 hours into a bill", it is "24 of 30 hours
62
+ you have already paid for" — money spent against money that will be
63
+ spent, which is a distinction the bar could not draw.
64
+ NO COLOUR OF ITS OWN, for the reason `threshold` has none: it has to
65
+ be legible over the track while never competing with the fill that
66
+ crosses it, and a token tuned for one of those is wrong for the other.
67
+ It is the fill's own paint at 30%, so it cannot be wrong in a theme,
68
+ a tone or a surface era has not seen, and it is the same 30% Slider's
69
+ `progress` has used for a seek bar's buffer since it shipped.
70
+ UNDER the fill, so it disappears as the value consumes it. That is the
71
+ reading, not a limitation: once you are past your included hours, the
72
+ included region IS spent, and `threshold` is what goes on marking
73
+ where it ended.
74
+ CLAMPED to the range rather than dropped, which is where it differs
75
+ from `threshold`. A region can be honestly truncated — a buffer past
76
+ `max` means the whole visible bar is accounted for, and painting it
77
+ all says exactly that. A point cannot: a mark pinned to the edge would
78
+ claim a boundary that is not in the range, so an out-of-range
79
+ `threshold` renders nothing instead.
80
+ Silent, like the mark. `aria-valuetext` already carries the reading,
81
+ and a second region a screen reader cannot position adds nothing.
82
+ (Slider spells its equivalent `progress` and takes a 0–1 FRACTION.
83
+ Here every positional prop is in the value's own units — `value`,
84
+ `max`, `threshold`, `buffer` — because a component that mixed the two
85
+ would be a bug generator. The names differ because on a Meter
86
+ `progress` would be a synonym for `value`.) |
87
+ | `start?` | `Snippet` | — | Captions under the two ends of the bar — what the range MEANS.
88
+ A meter states a proportion and leaves the reader to work out what of.
89
+ "0m used" on the left and "50h before approval" on the right is not
90
+ decoration, it is the reading; every consumer of a bare meter ends up
91
+ hand-rolling the same flex row underneath it, which is where this came
92
+ from. `start` is left, `end` is right, either may stand alone.
93
+ NOT ANNOUNCED. They are captions, so a screen reader gets the meter's
94
+ value and its own `aria-label`, not these. If the range needs saying
95
+ out loud, say it in the label too — the same rule `tone` carries.
96
+ THE DOM IS UNCHANGED WITHOUT THEM. With neither snippet this renders
97
+ exactly the element it always did, so `class` still lands on the track
98
+ and the geometry audits still walk the same box. Passing one wraps the
99
+ track in a column, and `class` still lands on the track. |
100
+ | `end?` | `Snippet` | — | — |
23
101
  | `ref?` | `(forwarded)` | `null` | bindable |
@@ -21,7 +21,7 @@ Inherits all props from `HTMLAttributes<HTMLElement>`.
21
21
  |------|------|---------|-------|
22
22
  | `ref?` | `HTMLElement \| null` | `null` | bindable |
23
23
  | `size?` | `'chip' \| 'control' \| 'bar'` | `'bar'` | Height tier, exactly as Bar's — the geometry is Bar's, not a second copy. |
24
- | `content?` | `'tier' \| 'pill'` | `'tier'` | Tier of the items inside, so the wall stays concentric to them. |
24
+ | `content?` | `BarContent` | | Tier of the items inside, so the wall stays concentric to them. |
25
25
  | `divider?` | `boolean` | `false` | Draw Bar's flush cap-a-panel rule beneath the bar. Off by default,
26
26
  because a nav that sits in a page already has whatever separation the
27
27
  layout gives it — and a link-variant nav in particular reads as text,
@@ -46,6 +46,13 @@ Inherits all props from `HTMLAnchorAttributes`.
46
46
  drops the chrome entirely and renders running text, for a primary nav
47
47
  that should read as links rather than a row of raised keycaps. Existing
48
48
  bars are byte-identical. |
49
+ | `currentCue?` | `'thickness' \| 'ink' \| 'none'` | `'thickness'` | WHAT MARKS THE CURRENT ITEM. `thickness` (default) is the three-state
50
+ decoration — portable across every theme and font because it is one
51
+ ink at three strengths. `ink` brightens the current item instead, and
52
+ is theme-dependent by construction: era's default themes point
53
+ --color-link at --color-bright, so its delta there is zero. `none`
54
+ silences era's cue entirely, for a consumer painting their own.
55
+ See navItemVariants above for the measurements behind each. |
49
56
  | `current?` | `boolean` | `false` | Whether this item points at the page you are on.
50
57
  There is deliberately no `active` prop beside this one. `active` would
51
58
  be a second source of truth for one fact, and the two drift the moment
@@ -15,6 +15,24 @@ Inherits all props from `Separator.RootProps`.
15
15
  | Prop | Type | Default | Notes |
16
16
  |------|------|---------|-------|
17
17
  | `variant?` | `'line' \| 'slant'` | `'line'` | Use `slant` for inline dividers in breadcrumbs, titles, and compact metadata. |
18
+ | `size?` | `keyof typeof SIZES` | — | Give a VERTICAL rule a height of its own, from the tier ladder, instead
19
+ of stretching to whatever line it lands in. Ignored when horizontal.
20
+ WHY YOU WOULD WANT IT. A stretched rule is a passenger: it has no
21
+ intrinsic height, so it measures whatever its neighbours measure and
22
+ contributes nothing back. That is right inside a Bar, whose height is
23
+ already fixed — and it is why a rule in an auto-height row cannot hold
24
+ that row's height steady. A consumer whose site header gained and lost
25
+ a qualifier between pages watched the chrome resize, and pinned a
26
+ `min-h-[calc(...)]` on the header to stop it. Sizing the rule removes
27
+ that calc: it becomes the piece that stands the row up.
28
+ (For the record, since the report named the wrong mechanism: a
29
+ stretched rule does not GROW its row. Measured in an 18px auto-height
30
+ row, adding one leaves the row at 18px and the rule resolves to 10px —
31
+ the line minus its two gutter margins. It can only ever be shorter
32
+ than the row it divides, which is the whole problem here.)
33
+ A sized rule drops the gutter margins with the stretch: the height is
34
+ now a deliberate number, and silently adding 2 x gutter to it would
35
+ make it the one element on the page you cannot reason about. |
18
36
  | `ref?` | `(forwarded)` | `null` | bindable |
19
37
 
20
38
  ## Usage
@@ -101,14 +101,17 @@
101
101
  "css": "@utility` cannot be nested inside one. Surface-scoped RULES stay\n * in their surface file; surface-scoped UTILITIES have to come out.\n *\n * @use the backdrop blur for a floating panel (popover, dialog, taskbar) — it\n * applies only inside a data-surface=\"glass\" subtree and is inert elsewhere,\n * so it is safe to leave on a panel that renders under every surface. */\n@utility glass-blur {\n\t[data-surface='glass'] & {\n\t\tbackdrop-filter: blur(var(--era-blur)) saturate(var(--era-saturate));\n\t\t-webkit-backdrop-filter: blur(var(--era-blur)) saturate(var(--era-saturate));\n\t}\n}"
102
102
  },
103
103
  {
104
- "name": "rounded-row",
104
+ "name": "tier-icon",
105
105
  "names": [
106
- "rounded-row"
106
+ "tier-icon",
107
+ "tier-pill",
108
+ "tier-chip",
109
+ "tier-control",
110
+ "tier-bar"
107
111
  ],
108
112
  "file": "index.css",
109
- "useCase": "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.",
110
- "description": "THE ROW RADIUS — concentricity for a row that is inset, WITH its inset intact.\n\nThe law is rd_outer − rd_inner = gap. A row inside a container that pads it by\nthe gutter therefore takes the container's radius MINUS the gutter — and no\ntier radius equals that at more than one density, which is why reaching for\n`rounded-control` and hoping looks wrong: in a control-radius pane the row and\nthe pane end up with identical curvature separated by 5px of gap, two arcs at\none corner with no relationship.\n\nThe alternative — deleting the gap so equal radii become legal — is the wrong\ntrade and was briefly shipped. The ladder exists so a gap and concentric\ncorners COEXIST; spending one to buy the other gives up the thing it is for.\n\nThe calc runs on the ROW, not on the container, which is what makes it\nautomatic: `--era-container-rd` is inherited, so a container that is not\ncontrol-radius publishes its own and every row inside re-derives. era's\ncommand palette does exactly that — it is rd-bar, so its rows resolve to\nrd-bar − gap, which IS rd-control (control-inset-bar is the gutter), and the\npalette's geometry is unchanged by this.",
111
- "css": "@utility rounded-row {\n\tborder-radius: max(0px, calc(var(--era-container-rd, var(--era-rd-control)) - var(--era-gap)));\n}"
113
+ "description": "BOUNDED GEOMETRY — height and radius are one decision.\n\nA tier is a geometric type, not two classes a caller must remember to keep in\nsync. `tier-control` establishes h-control AND rd-control; the radius ladder\nthen guarantees rd_outer rd_inner = (h_outer h_inner)/2 for every pair.\nWidth remains the component's decision: a row stretches, a button adds\nmin-w-control, and a square control adds w-control.",
114
+ "css": "@utility tier-icon {\n\theight: var(--era-h-icon);\n\tborder-radius: var(--era-rd-icon);\n}\n\n@utility tier-pill {\n\theight: var(--era-h-pill);\n\tborder-radius: var(--era-rd-pill);\n}\n\n@utility tier-chip {\n\theight: var(--era-h-chip);\n\tborder-radius: var(--era-rd-chip);\n}\n\n@utility tier-control {\n\theight: var(--era-h-control);\n\tborder-radius: var(--era-rd-control);\n}\n\n@utility tier-bar {\n\theight: var(--era-h-bar);\n\tborder-radius: var(--era-rd-bar);\n}"
112
115
  },
113
116
  {
114
117
  "name": "h-line",
@@ -120,6 +123,16 @@
120
123
  "description": "One LINE BOX tall — the height of the text this stands in for.\n\nSkeleton's `line` used h-(--era-text), which is the body FONT SIZE: never the\nheight of a rendered line (that is font-size x leading), and wrong again by\nthe ladder's ratio for any rung other than body. `1lh` resolves against the\nelement's OWN computed line-height, so putting a text-* class on the skeleton\nmakes it match that tier exactly, at every density, and it follows a leading\nchange for free.",
121
124
  "css": "@utility h-line {\n\theight: 1lh;\n}"
122
125
  },
126
+ {
127
+ "name": "era-track-well",
128
+ "names": [
129
+ "era-track-well"
130
+ ],
131
+ "file": "index.css",
132
+ "useCase": "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.",
133
+ "description": "A WELL THAT SURVIVES BEING FILLED.\n\n`shadow-well` is an INSET shadow, and an inset shadow belongs to the\nelement's background layer — it paints under the element's own descendants.\nThat is invisible on a well holding text (a checkbox, a code block: the\nchrome runs around the content) and wrong the moment something paints the\nwhole box. Meter, Progress and the Slider track all do exactly that: an\nopaque child, `translateX`-ed to the value. So the well survived only where\nthe track was EMPTY — a half-full meter on bevel had a chisel to the right\nof the value and none to the left, which reads as a hole with a sticker\nover it rather than a level inside a well.\n\nThe fix is painting order, not geometry: the same `--era-shadow-well` on a\npseudo-element that is the element's LAST child, so it lands above the fill.\nThe fill still spans the full box — nothing is inset, no radius is\nre-derived, and 0% and 100% keep their exact ends. The `position: relative`\nis load-bearing (the overlay resolves against this box) and `inherit` on the\nradius keeps it honest under `data-corners=\"square\"`.\n\nSurface-agnostic on purpose. It reads the same token every well reads, so\nglass's hairline rim is restored by the same line that restores bevel's\nchisel, and flat — where the token is `none` — renders exactly as before.\nReported against bevel by a consumer; it was never a bevel bug.",
134
+ "css": "@utility era-track-well {\n\tposition: relative;\n\t&::after {\n\t\tcontent: '';\n\t\tposition: absolute;\n\t\tinset: 0;\n\t\tborder-radius: inherit;\n\t\tbox-shadow: var(--era-shadow-well);\n\t\tpointer-events: none;\n\t}\n}"
135
+ },
123
136
  {
124
137
  "name": "era-skeleton-pulse",
125
138
  "names": [
@@ -433,12 +446,16 @@
433
446
  "p-chip-inset-bar",
434
447
  "px-chip-inset-bar",
435
448
  "py-chip-inset-bar",
436
- "gap-chip-inset-bar"
449
+ "gap-chip-inset-bar",
450
+ "p-control-inset-bar",
451
+ "px-control-inset-bar",
452
+ "py-control-inset-bar",
453
+ "gap-control-inset-bar"
437
454
  ],
438
455
  "file": "index.css",
439
456
  "useCase": "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.",
440
457
  "description": "",
441
- "css": "@theme inline {\n\t--spacing-icon-inset-pill: var(--era-icon-inset-pill);\n\t--spacing-icon-inset-chip: var(--era-icon-inset-chip);\n\t--spacing-icon-inset-control: var(--era-icon-inset-control);\n\t--spacing-icon-inset-bar: var(--era-icon-inset-bar);\n\t--spacing-pill-inset-chip: var(--era-pill-inset-chip);\n\t--spacing-pill-inset-control: var(--era-pill-inset-control);\n\t--spacing-pill-inset-bar: var(--era-pill-inset-bar);\n\t--spacing-chip-inset-control: var(--era-chip-inset-control);\n\t--spacing-chip-inset-bar: var(--era-chip-inset-bar);\n}"
458
+ "css": "@theme inline {\n\t--spacing-icon-inset-pill: var(--era-icon-inset-pill);\n\t--spacing-icon-inset-chip: var(--era-icon-inset-chip);\n\t--spacing-icon-inset-control: var(--era-icon-inset-control);\n\t--spacing-icon-inset-bar: var(--era-icon-inset-bar);\n\t--spacing-pill-inset-chip: var(--era-pill-inset-chip);\n\t--spacing-pill-inset-control: var(--era-pill-inset-control);\n\t--spacing-pill-inset-bar: var(--era-pill-inset-bar);\n\t--spacing-chip-inset-control: var(--era-chip-inset-control);\n\t--spacing-chip-inset-bar: var(--era-chip-inset-bar);\n\t--spacing-control-inset-bar: var(--era-control-inset-bar);\n}"
442
459
  },
443
460
  {
444
461
  "name": "p-glyph-icon",
@@ -590,11 +607,17 @@
590
607
  "border-warning",
591
608
  "bg-warning-fg",
592
609
  "text-warning-fg",
593
- "border-warning-fg"
610
+ "border-warning-fg",
611
+ "bg-info",
612
+ "text-info",
613
+ "border-info",
614
+ "bg-info-fg",
615
+ "text-info-fg",
616
+ "border-info-fg"
594
617
  ],
595
618
  "file": "index.css",
596
619
  "description": "The standing pill fill — --era-fill, the latched counterpart of the\ntransient highlight. Surface-owned: a composing wash on flat/bevel, the\nopaque swatch on glass.",
597
- "css": "@theme inline {\n\t--color-fill: var(--era-fill);\n\t--color-link: var(--color-link);\n\t--color-primary: var(--color-primary);\n\t--color-primary-fg: var(--color-primary-fg);\n\t--color-primary-hover: var(--color-primary-hover);\n\t--color-destructive: var(--color-destructive);\n\t--color-destructive-fg: var(--color-destructive-fg);\n\t--color-success: var(--color-success);\n\t--color-success-fg: var(--color-success-fg);\n\t--color-warning: var(--color-warning);\n\t--color-warning-fg: var(--color-warning-fg);\n}"
620
+ "css": "@theme inline {\n\t--color-fill: var(--era-fill);\n\t--color-link: var(--color-link);\n\t--color-primary: var(--color-primary);\n\t--color-primary-fg: var(--color-primary-fg);\n\t--color-primary-hover: var(--color-primary-hover);\n\t--color-destructive: var(--color-destructive);\n\t--color-destructive-fg: var(--color-destructive-fg);\n\t--color-success: var(--color-success);\n\t--color-success-fg: var(--color-success-fg);\n\t--color-warning: var(--color-warning);\n\t--color-warning-fg: var(--color-warning-fg);\n\t--color-info: var(--color-info);\n\t--color-info-fg: var(--color-info-fg);\n}"
598
621
  }
599
622
  ]
600
623
  }