@signal9/era-ui 10.0.0 → 10.1.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 (47) hide show
  1. package/dist/ai/index.js +1 -1
  2. package/dist/ai/message/message-usage.svelte +1 -1
  3. package/dist/ai/prompt-input/prompt-input-attachments.svelte +1 -1
  4. package/dist/ai/run-status/run-status.svelte +3 -2
  5. package/dist/ai/swarm/swarm-agent.svelte +2 -2
  6. package/dist/ai/swarm/swarm-artifact.svelte +1 -1
  7. package/dist/ai/swarm/swarm-barrier.svelte +1 -1
  8. package/dist/ai/swarm/swarm-footer.svelte +3 -2
  9. package/dist/ai/swarm/swarm-header.svelte +1 -1
  10. package/dist/ai/swarm/swarm-node.svelte +1 -1
  11. package/dist/ai/swarm/swarm-role.svelte +5 -2
  12. package/dist/ai/tool/tool-chip.svelte +1 -1
  13. package/dist/apps/index.d.ts +3 -3
  14. package/dist/apps/index.js +3 -3
  15. package/dist/apps/notes/editor/slash-menu.svelte +2 -4
  16. package/dist/apps/notes/notes.svelte +1 -1
  17. package/dist/dev/audit/audits/anonymous-text.d.ts +2 -0
  18. package/dist/dev/audit/audits/anonymous-text.js +73 -0
  19. package/dist/dev/audit/audits/index.d.ts +2 -1
  20. package/dist/dev/audit/audits/index.js +3 -1
  21. package/dist/dev/audit/types.d.ts +1 -1
  22. package/dist/docs/css-utilities.js +1 -1
  23. package/dist/docs/llms-txt.js +2 -2
  24. package/dist/docs/notes.md +3 -3
  25. package/dist/era-ui.css +1 -1
  26. package/dist/generated-docs/llms-full.txt +96 -16
  27. package/dist/generated-docs/llms.txt +11 -7
  28. package/dist/generated-docs/manifest.json +28 -7
  29. package/dist/generated-docs/notes.md +3 -3
  30. package/dist/generated-docs/utilities.json +23 -4
  31. package/dist/generated-docs/utilities.md +82 -6
  32. package/dist/styles/index.css +43 -5
  33. package/dist/ui/bar/bar.svelte +1 -1
  34. package/dist/ui/bar/bar.svelte.d.ts +1 -1
  35. package/dist/ui/button/button.svelte +12 -1
  36. package/dist/ui/button/variants.d.ts +1 -1
  37. package/dist/ui/button/variants.js +1 -1
  38. package/dist/ui/chip/chip.svelte +13 -3
  39. package/dist/ui/chip/chip.svelte.d.ts +1 -1
  40. package/dist/ui/command/command-item.svelte +1 -1
  41. package/dist/ui/command/command-link-item.svelte +1 -1
  42. package/dist/ui/command-bar/command-bar.svelte +1 -1
  43. package/dist/ui/index.js +1 -1
  44. package/dist/ui/kv/kv.svelte +1 -1
  45. package/dist/ui/select/select-trigger.svelte +1 -1
  46. package/dist/ui/step/step-summary.svelte +1 -1
  47. package/package.json +1 -1
@@ -4,18 +4,20 @@
4
4
 
5
5
  ## Using the library
6
6
 
7
+ This package is published to two registries under two names: `@signal9/era-ui` on npm (used throughout these docs) and `@sig-nine/era-ui` on GitHub Packages, where the scope must match the repo owner. Same code, same versions — pick the one your registry serves and keep the scope consistent.
8
+
7
9
  **One entry point per component.** There is no barrel that re-exports everything — importing a component can never drag in another one, or another one's dependencies.
8
10
 
9
11
  ```ts
10
- import { Button } from "@sig-nine/era-ui/button";
11
- import * as Select from "@sig-nine/era-ui/select";
12
- import * as Table from "@sig-nine/era-ui/table";
12
+ import { Button } from "@signal9/era-ui/button";
13
+ import * as Select from "@signal9/era-ui/select";
14
+ import * as Table from "@signal9/era-ui/table";
13
15
 
14
16
  // The root is the SYSTEM layer only: cn/tv, keybindings, the axis
15
17
  // registry, and Mode. It imports no component.
16
- import { cn, ModeProvider } from "@sig-nine/era-ui";
18
+ import { cn, ModeProvider } from "@signal9/era-ui";
17
19
 
18
- import "@sig-nine/era-ui/styles"; // source CSS, scanned by your Tailwind
20
+ import "@signal9/era-ui/styles"; // source CSS, scanned by your Tailwind
19
21
  ```
20
22
 
21
23
  The path is always the component's own kebab-case name — `/button`, `/dropdown-menu`, `/date-range-picker` — and the other layers are namespaced: `/ai` and `/ai/<part>`, `/apps/<app>`, `/os`, `/audit`.
@@ -24,7 +26,7 @@ Named exports wrap a single primitive (`Button`, `Badge`, `Chip`, `Input`, …).
24
26
 
25
27
  ### Stylesheets
26
28
 
27
- `@sig-nine/era-ui/styles` is the source CSS — import it once and your own Tailwind build scans era's components and emits only the utilities they use. `@sig-nine/era-ui/css` is the prebuilt alternative for a project without Tailwind. Three more are opt-in, each single-purpose: `/styles/video-player` (vidstack's vendor CSS, needed only with `VideoPlayer`), `/styles/prose` (document typography for a TipTap editor — put `era-notes-prose` on your editor element; `NoteEditor` imports it already), and `/styles/themes` (the raw theme tokens).
29
+ `@signal9/era-ui/styles` is the source CSS — import it once and your own Tailwind build scans era's components and emits only the utilities they use. `@signal9/era-ui/css` is the prebuilt alternative for a project without Tailwind. Three more are opt-in, each single-purpose: `/styles/video-player` (vidstack's vendor CSS, needed only with `VideoPlayer`), `/styles/prose` (document typography for a TipTap editor — put `era-notes-prose` on your editor element; `NoteEditor` imports it already), and `/styles/themes` (the raw theme tokens).
28
30
 
29
31
  ### Optional peers
30
32
 
@@ -58,7 +60,9 @@ Classes, not components — they need no import beyond the stylesheet. Reach for
58
60
  - `era-text-trim` — vertically center single-line control text (a label beside an icon, a button's own text) without a translateY nudge. Fixed-height, non-editable elements ONLY: never on an <input>/<textarea>, and never on the same element as `truncate` or any overflow clip — put it on the fixed-height ROW and let the truncating child centre inside it. See era-text-trim-caps for ALL-CAPS.
59
61
  - `truncate` — single-line horizontal truncation with an ellipsis — put it on the truncating CHILD inside a trimmed or leading-none row, never on the same element as era-text-trim; it will not clip ascenders or descenders.
60
62
  - `era-text-trim-caps` — the era-text-trim variant for ALL-CAPS single-line strings (kbd hints, acronym badges) — never for mixed-case text (a label like "System" reads high; use era-text-trim), and never on the same element as `truncate`.
61
- - `era-token-ink · era-ink-center` — the ink centring for a token pill a badge, a KV value, any short caps-or-digits label in a fixed-height box; it needs no icon sibling (with one the box must stay flex, and the general trim applies).
63
+ - `era-ink-center` — on a native single-line <input> (or the text layer that must mirror one) inside a fixed-height field never alongside era-text-trim.
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
+ - `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).
62
66
  - `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.
63
67
  - `era-collapse` — the height sweep for a bits-ui Collapsible.Content — put it on the Content element itself (it carries its own overflow: hidden).
64
68
  - `h-icon · h-pill · h-chip · h-control · h-bar` — the tier-height ladder — h-icon (icon) · h-pill (nested pill) · h-chip (standalone pill) · h-control (control) · h-bar (bar); the named form of h-(--era-h-*).
@@ -131,7 +135,7 @@ never the `h-(--era-h-control)` var-class spelling. Everything below rides along
131
135
  with the stylesheet and needs no import:
132
136
 
133
137
  ```ts
134
- import "@sig-nine/era-ui/css";
138
+ import "@signal9/era-ui/css";
135
139
  ```
136
140
 
137
141
  ```svelte
@@ -166,7 +170,9 @@ enough to stay var-form — `min-w-(--era-h-control)` etc. remain the escape hat
166
170
  | `era-text-trim` | vertically center single-line control text (a label beside an icon, a button's own text) without a translateY nudge. Fixed-height, non-editable elements ONLY: never on an <input>/<textarea>, and never on the same element as `truncate` or any overflow clip — put it on the fixed-height ROW and let the truncating child centre inside it. See era-text-trim-caps for ALL-CAPS. |
167
171
  | `truncate` | single-line horizontal truncation with an ellipsis — put it on the truncating CHILD inside a trimmed or leading-none row, never on the same element as era-text-trim; it will not clip ascenders or descenders. |
168
172
  | `era-text-trim-caps` | the era-text-trim variant for ALL-CAPS single-line strings (kbd hints, acronym badges) — never for mixed-case text (a label like "System" reads high; use era-text-trim), and never on the same element as `truncate`. |
169
- | `era-token-ink` `era-ink-center` | the ink centring for a token pill a badge, a KV value, any short caps-or-digits label in a fixed-height box; it needs no icon sibling (with one the box must stay flex, and the general trim applies). |
173
+ | `era-ink-center` | on a native single-line <input> (or the text layer that must mirror one) inside a fixed-height field never alongside era-text-trim. |
174
+ | `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. |
175
+ | `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). |
170
176
  | `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. |
171
177
  | `era-collapse` | the height sweep for a bits-ui Collapsible.Content — put it on the Content element itself (it carries its own overflow: hidden). |
172
178
  | `h-icon` `h-pill` `h-chip` `h-control` `h-bar` | the tier-height ladder — h-icon (icon) · h-pill (nested pill) · h-chip (standalone pill) · h-control (control) · h-bar (bar); the named form of h-(--era-h-*). |
@@ -357,9 +363,47 @@ Declared in `index.css`.
357
363
  }
358
364
  ```
359
365
 
360
- ## era-token-ink · era-ink-center
366
+ ## era-ink-center
367
+
368
+ **Use it for:** on a native single-line <input> (or the text layer that must mirror one) inside a fixed-height field — never alongside era-text-trim.
369
+
370
+ The EDITABLE counterpart of era-text-trim — for native <input>s, which the
371
+ trim is forbidden on (it collapses the UA's inner editor box and slices the
372
+ glyphs; see typography/collapsed-text-trim).
373
+
374
+ Without help, a tier-height input centres its text by FONT BOX, and the ui
375
+ stacks' font box is deeply asymmetric: ascent ≈1.07em against descent
376
+ ≈0.29em. Centring that box parks the visible band (caps, x-height) ~0.115em
377
+ LOW — so a field's text sat visibly below the trimmed label beside it.
378
+
379
+ The value is OPTICAL, not the derivation's: pure x-band centring computes
380
+ to ~0.23em of padding, but pixel-measured against real Title-Case text it
381
+ over-lifts — the eye weighs the cap-to-baseline mass, which x-band
382
+ centring parks ~2px high (caps 2.75px off the top against 5.7px of air
383
+ under the baseline, measured at dense). 0.08em splits the difference
384
+ between the x-band ideal and the cap-band ideal, landing caps ~3.7px in an
385
+ 18px field with ~4.7px under the baseline — the reading where neither
386
+ band's partisans win and neither complains.
387
+
388
+ The lift is padding, not a translateY: an icon-less Input paints the field
389
+ chrome itself, and a transform would drag the field along with the ink.
390
+ padding-bottom shrinks the content box from below, and a single-line input
391
+ re-centres its line box in what remains — the ink rises by exactly half the
392
+ padding, the chrome doesn't move, and the caret and selection follow the
393
+ text. In em on purpose: the correction is a fraction of the type, so it
394
+ holds across every density and text size with one value.
395
+
396
+ Declared in `index.css`.
397
+
398
+ ```css
399
+ @utility era-ink-center {
400
+ padding-bottom: 0.08em;
401
+ }
402
+ ```
403
+
404
+ ## era-token-ink
361
405
 
362
- **Use it for:** the ink centring for a token pill — a badge, a KV value, any short caps-or-digits label in a fixed-height box; it needs no icon sibling (with one the box must stay flex, and the general trim applies).
406
+ **Use it for:** 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.
363
407
 
364
408
  TOKEN INK — the ink centring for a pill whose content is a TOKEN: an id, a
365
409
  count, a status, a tag. Font-independent, unlike everything it replaces.
@@ -398,9 +442,45 @@ Declared in `index.css`.
398
442
  line-height: 1;
399
443
  text-box: trim-both cap alphabetic;
400
444
  }
445
+ ```
401
446
 
402
- @utility era-ink-center {
403
- padding-bottom: 0.08em;
447
+ ## era-label-ink
448
+
449
+ **Use it for:** 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).
450
+
451
+ LABEL INK — era-token-ink's mixed-case sibling, and the general answer to
452
+ "this fixed-height control holds one line of text".
453
+
454
+ Same mechanism, same reason: `text-box-trim` needs a BLOCK box, so the row
455
+ becomes one and `align-content` (block-container alignment) does the
456
+ centring. What differs is the band and the alignment, because the content
457
+ class differs — a label is sentence case, so the x-height band is the visual
458
+ mass to centre (cap-band reads low on "System"), and it is laid out against
459
+ the row's padding rather than centred in it.
460
+
461
+ WHY IT EXISTS AT ALL: era's controls are flex, and a flex container's bare
462
+ text is an ANONYMOUS flex item — a box no selector reaches and no
463
+ non-inherited property touches. So `era-text-trim` on a flex row is inert,
464
+ contributing only its `line-height: 1`, and the ink falls back to line-box
465
+ centring by font metrics. era-token-ink already fixed that for centred
466
+ tokens; this fixes it for everything else.
467
+
468
+ THE ICON CASE IS NOT SOLVED, and cannot be from CSS: an icon beside the text
469
+ needs the box to stay flex, which re-creates the anonymous item. A component
470
+ whose children are an opaque slot therefore guards it —
471
+ `not-has-[svg]:era-label-ink` — and ink-centres the text-only case while
472
+ leaving the icon case on line-box centring. Splitting icon and label into
473
+ separate slots is what would close it; typography/inert-trim reports every
474
+ site still on the fallback.
475
+
476
+ Declared in `index.css`.
477
+
478
+ ```css
479
+ @utility era-label-ink {
480
+ display: block;
481
+ align-content: center;
482
+ line-height: 1;
483
+ text-box: trim-both ex alphabetic;
404
484
  }
405
485
  ```
406
486
 
@@ -755,13 +835,13 @@ A complete notes app — rich-text editor, slash commands, local persistence.
755
835
 
756
836
  ## Usage
757
837
 
758
- Apps ship on their own subpath and are **not** re-exported from `@sig-nine/era-ui`.
838
+ Apps ship on their own subpath and are **not** re-exported from `@signal9/era-ui`.
759
839
  Notes carries TipTap, and a consumer who never imports an app should never pay
760
840
  for it.
761
841
 
762
842
  ```svelte
763
843
  <script>
764
- import { Notes } from '@sig-nine/era-ui/apps';
844
+ import { Notes } from '@signal9/era-ui/apps';
765
845
  </script>
766
846
 
767
847
  <Notes />
@@ -801,7 +881,7 @@ Persistence is one small interface, so server-backed hosting is a drop-in swap
801
881
  `load`/`save` may be sync or async:
802
882
 
803
883
  ```ts
804
- import { Notes, NotesStore } from '@sig-nine/era-ui/apps';
884
+ import { Notes, NotesStore } from '@signal9/era-ui/apps';
805
885
 
806
886
  const store = new NotesStore({
807
887
  adapter: {
@@ -38,18 +38,20 @@ Full derivations: `{{ORIGIN}}/spacing.md` (the ladder and every token), `{{ORIGI
38
38
 
39
39
  ## Using the library
40
40
 
41
+ This package is published to two registries under two names: `@signal9/era-ui` on npm (used throughout these docs) and `@sig-nine/era-ui` on GitHub Packages, where the scope must match the repo owner. Same code, same versions — pick the one your registry serves and keep the scope consistent.
42
+
41
43
  **One entry point per component.** There is no barrel that re-exports everything — importing a component can never drag in another one, or another one's dependencies.
42
44
 
43
45
  ```ts
44
- import { Button } from "@sig-nine/era-ui/button";
45
- import * as Select from "@sig-nine/era-ui/select";
46
- import * as Table from "@sig-nine/era-ui/table";
46
+ import { Button } from "@signal9/era-ui/button";
47
+ import * as Select from "@signal9/era-ui/select";
48
+ import * as Table from "@signal9/era-ui/table";
47
49
 
48
50
  // The root is the SYSTEM layer only: cn/tv, keybindings, the axis
49
51
  // registry, and Mode. It imports no component.
50
- import { cn, ModeProvider } from "@sig-nine/era-ui";
52
+ import { cn, ModeProvider } from "@signal9/era-ui";
51
53
 
52
- import "@sig-nine/era-ui/styles"; // source CSS, scanned by your Tailwind
54
+ import "@signal9/era-ui/styles"; // source CSS, scanned by your Tailwind
53
55
  ```
54
56
 
55
57
  The path is always the component's own kebab-case name — `/button`, `/dropdown-menu`, `/date-range-picker` — and the other layers are namespaced: `/ai` and `/ai/<part>`, `/apps/<app>`, `/os`, `/audit`.
@@ -58,7 +60,7 @@ Named exports wrap a single primitive (`Button`, `Badge`, `Chip`, `Input`, …).
58
60
 
59
61
  ### Stylesheets
60
62
 
61
- `@sig-nine/era-ui/styles` is the source CSS — import it once and your own Tailwind build scans era's components and emits only the utilities they use. `@sig-nine/era-ui/css` is the prebuilt alternative for a project without Tailwind. Three more are opt-in, each single-purpose: `/styles/video-player` (vidstack's vendor CSS, needed only with `VideoPlayer`), `/styles/prose` (document typography for a TipTap editor — put `era-notes-prose` on your editor element; `NoteEditor` imports it already), and `/styles/themes` (the raw theme tokens).
63
+ `@signal9/era-ui/styles` is the source CSS — import it once and your own Tailwind build scans era's components and emits only the utilities they use. `@signal9/era-ui/css` is the prebuilt alternative for a project without Tailwind. Three more are opt-in, each single-purpose: `/styles/video-player` (vidstack's vendor CSS, needed only with `VideoPlayer`), `/styles/prose` (document typography for a TipTap editor — put `era-notes-prose` on your editor element; `NoteEditor` imports it already), and `/styles/themes` (the raw theme tokens).
62
64
 
63
65
  ### Optional peers
64
66
 
@@ -96,7 +98,9 @@ Classes, not components — they need no import beyond the stylesheet. Reach for
96
98
  - `era-text-trim` — vertically center single-line control text (a label beside an icon, a button's own text) without a translateY nudge. Fixed-height, non-editable elements ONLY: never on an <input>/<textarea>, and never on the same element as `truncate` or any overflow clip — put it on the fixed-height ROW and let the truncating child centre inside it. See era-text-trim-caps for ALL-CAPS.
97
99
  - `truncate` — single-line horizontal truncation with an ellipsis — put it on the truncating CHILD inside a trimmed or leading-none row, never on the same element as era-text-trim; it will not clip ascenders or descenders.
98
100
  - `era-text-trim-caps` — the era-text-trim variant for ALL-CAPS single-line strings (kbd hints, acronym badges) — never for mixed-case text (a label like "System" reads high; use era-text-trim), and never on the same element as `truncate`.
99
- - `era-token-ink · era-ink-center` — the ink centring for a token pill a badge, a KV value, any short caps-or-digits label in a fixed-height box; it needs no icon sibling (with one the box must stay flex, and the general trim applies).
101
+ - `era-ink-center` — on a native single-line <input> (or the text layer that must mirror one) inside a fixed-height field never alongside era-text-trim.
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
+ - `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).
100
104
  - `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.
101
105
  - `era-collapse` — the height sweep for a bits-ui Collapsible.Content — put it on the Content element itself (it carries its own overflow: hidden).
102
106
  - `h-icon · h-pill · h-chip · h-control · h-bar` — the tier-height ladder — h-icon (icon) · h-pill (nested pill) · h-chip (standalone pill) · h-control (control) · h-bar (bar); the named form of h-(--era-h-*).
@@ -44,7 +44,7 @@
44
44
  "slug": "utilities",
45
45
  "title": "Utilities",
46
46
  "summary": "The CSS classes era ships alongside the components — links, ink-centred text, shimmer, hidden scrollbars.",
47
- "tokenEstimate": 6147,
47
+ "tokenEstimate": 7162,
48
48
  "sections": [
49
49
  "Overview",
50
50
  "era-interactive",
@@ -53,7 +53,9 @@
53
53
  "era-text-trim",
54
54
  "truncate",
55
55
  "era-text-trim-caps",
56
- "era-token-ink · era-ink-center",
56
+ "era-ink-center",
57
+ "era-token-ink",
58
+ "era-label-ink",
57
59
  "era-shimmer",
58
60
  "era-collapse",
59
61
  "h-icon · h-pill · h-chip · h-control · h-bar",
@@ -107,7 +109,7 @@
107
109
  "slug": "notes",
108
110
  "title": "Notes",
109
111
  "summary": "A complete notes app — rich-text editor, slash commands, local persistence.",
110
- "tokenEstimate": 885,
112
+ "tokenEstimate": 884,
111
113
  "sections": [
112
114
  "Usage",
113
115
  "Peer dependencies",
@@ -1022,15 +1024,34 @@
1022
1024
  "css": "@utility era-text-trim-caps {\n\tline-height: 1;\n\ttext-box: trim-both cap alphabetic;\n}"
1023
1025
  },
1024
1026
  {
1025
- "name": "era-token-ink",
1027
+ "name": "era-ink-center",
1026
1028
  "names": [
1027
- "era-token-ink",
1028
1029
  "era-ink-center"
1029
1030
  ],
1030
1031
  "file": "index.css",
1031
- "useCase": "the ink centring for a token pill a badge, a KV value, any short caps-or-digits label in a fixed-height box; it needs no icon sibling (with one the box must stay flex, and the general trim applies).",
1032
+ "useCase": "on a native single-line <input> (or the text layer that must mirror one) inside a fixed-height field never alongside era-text-trim.",
1033
+ "description": "The EDITABLE counterpart of era-text-trim — for native <input>s, which the\ntrim is forbidden on (it collapses the UA's inner editor box and slices the\nglyphs; see typography/collapsed-text-trim).\n\nWithout help, a tier-height input centres its text by FONT BOX, and the ui\nstacks' font box is deeply asymmetric: ascent ≈1.07em against descent\n≈0.29em. Centring that box parks the visible band (caps, x-height) ~0.115em\nLOW — so a field's text sat visibly below the trimmed label beside it.\n\nThe value is OPTICAL, not the derivation's: pure x-band centring computes\nto ~0.23em of padding, but pixel-measured against real Title-Case text it\nover-lifts — the eye weighs the cap-to-baseline mass, which x-band\ncentring parks ~2px high (caps 2.75px off the top against 5.7px of air\nunder the baseline, measured at dense). 0.08em splits the difference\nbetween the x-band ideal and the cap-band ideal, landing caps ~3.7px in an\n18px field with ~4.7px under the baseline — the reading where neither\nband's partisans win and neither complains.\n\nThe lift is padding, not a translateY: an icon-less Input paints the field\nchrome itself, and a transform would drag the field along with the ink.\npadding-bottom shrinks the content box from below, and a single-line input\nre-centres its line box in what remains — the ink rises by exactly half the\npadding, the chrome doesn't move, and the caret and selection follow the\ntext. In em on purpose: the correction is a fraction of the type, so it\nholds across every density and text size with one value.",
1034
+ "css": "@utility era-ink-center {\n\tpadding-bottom: 0.08em;\n}"
1035
+ },
1036
+ {
1037
+ "name": "era-token-ink",
1038
+ "names": [
1039
+ "era-token-ink"
1040
+ ],
1041
+ "file": "index.css",
1042
+ "useCase": "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.",
1032
1043
  "description": "TOKEN INK — the ink centring for a pill whose content is a TOKEN: an id, a\ncount, a status, a tag. Font-independent, unlike everything it replaces.\n\nTHE BUG IT FIXES, which had been shipping library-wide and invisibly:\n`text-box-trim` DOES NOTHING ON A FLEX CONTAINER. Era's controls are all\nflex, and a flex container's bare text becomes an ANONYMOUS flex item — a box\nno selector can reach and no non-inherited property can touch. So\n`era-text-trim` on a Badge, a Chip, a Button or a Bar contributed exactly one\nthing: `line-height: 1`. The ink was then centred by `align-items: center`\non the LINE BOX, which is precisely the font-metric-dependent centring the\ntrim exists to eliminate. Measured on the shipped mono stack: a badge reading\n\"AC-2.1\" sat 2px high in an 18px pill, and no amount of tuning the trim's\nBAND changed it, because the trim was never running.\n\nThe mechanism here restores it: a BLOCK box (where text-box-trim does apply)\nwhose trimmed line box is then centred by `align-content` — block-container\nalignment, so no flex formatting context and no anonymous item. The browser\ncomputes the band from the font's own metrics, which is what makes it hold\nacross ui-monospace, system-ui and ui-serif instead of needing a constant\nper stack.\n\nCAP band, not the x-height band the general trim uses, because the content\nclass is different: a token is caps and digits (both cap-height), and\ncentring the x-band parks them high by (cap − x)/2. Measured on the shipped\nstacks, cap-band centring takes an id/count/status from −2.0px to 0.0.\nProse and sentence-case labels keep era-text-trim; this is for tokens.",
1033
- "css": "@utility era-token-ink {\n\tdisplay: block;\n\talign-content: center;\n\ttext-align: center;\n\tline-height: 1;\n\ttext-box: trim-both cap alphabetic;\n}\n\n@utility era-ink-center {\n\tpadding-bottom: 0.08em;\n}"
1044
+ "css": "@utility era-token-ink {\n\tdisplay: block;\n\talign-content: center;\n\ttext-align: center;\n\tline-height: 1;\n\ttext-box: trim-both cap alphabetic;\n}"
1045
+ },
1046
+ {
1047
+ "name": "era-label-ink",
1048
+ "names": [
1049
+ "era-label-ink"
1050
+ ],
1051
+ "file": "index.css",
1052
+ "useCase": "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).",
1053
+ "description": "LABEL INK — era-token-ink's mixed-case sibling, and the general answer to\n\"this fixed-height control holds one line of text\".\n\nSame mechanism, same reason: `text-box-trim` needs a BLOCK box, so the row\nbecomes one and `align-content` (block-container alignment) does the\ncentring. What differs is the band and the alignment, because the content\nclass differs — a label is sentence case, so the x-height band is the visual\nmass to centre (cap-band reads low on \"System\"), and it is laid out against\nthe row's padding rather than centred in it.\n\nWHY IT EXISTS AT ALL: era's controls are flex, and a flex container's bare\ntext is an ANONYMOUS flex item — a box no selector reaches and no\nnon-inherited property touches. So `era-text-trim` on a flex row is inert,\ncontributing only its `line-height: 1`, and the ink falls back to line-box\ncentring by font metrics. era-token-ink already fixed that for centred\ntokens; this fixes it for everything else.\n\nTHE ICON CASE IS NOT SOLVED, and cannot be from CSS: an icon beside the text\nneeds the box to stay flex, which re-creates the anonymous item. A component\nwhose children are an opaque slot therefore guards it —\n`not-has-[svg]:era-label-ink` — and ink-centres the text-only case while\nleaving the icon case on line-box centring. Splitting icon and label into\nseparate slots is what would close it; typography/inert-trim reports every\nsite still on the fallback.",
1054
+ "css": "@utility era-label-ink {\n\tdisplay: block;\n\talign-content: center;\n\tline-height: 1;\n\ttext-box: trim-both ex alphabetic;\n}"
1034
1055
  },
1035
1056
  {
1036
1057
  "name": "era-shimmer",
@@ -4,13 +4,13 @@ A complete notes app — rich-text editor, slash commands, local persistence.
4
4
 
5
5
  ## Usage
6
6
 
7
- Apps ship on their own subpath and are **not** re-exported from `@sig-nine/era-ui`.
7
+ Apps ship on their own subpath and are **not** re-exported from `@signal9/era-ui`.
8
8
  Notes carries TipTap, and a consumer who never imports an app should never pay
9
9
  for it.
10
10
 
11
11
  ```svelte
12
12
  <script>
13
- import { Notes } from '@sig-nine/era-ui/apps';
13
+ import { Notes } from '@signal9/era-ui/apps';
14
14
  </script>
15
15
 
16
16
  <Notes />
@@ -50,7 +50,7 @@ Persistence is one small interface, so server-backed hosting is a drop-in swap
50
50
  `load`/`save` may be sync or async:
51
51
 
52
52
  ```ts
53
- import { Notes, NotesStore } from '@sig-nine/era-ui/apps';
53
+ import { Notes, NotesStore } from '@signal9/era-ui/apps';
54
54
 
55
55
  const store = new NotesStore({
56
56
  adapter: {
@@ -61,15 +61,34 @@
61
61
  "css": "@utility era-text-trim-caps {\n\tline-height: 1;\n\ttext-box: trim-both cap alphabetic;\n}"
62
62
  },
63
63
  {
64
- "name": "era-token-ink",
64
+ "name": "era-ink-center",
65
65
  "names": [
66
- "era-token-ink",
67
66
  "era-ink-center"
68
67
  ],
69
68
  "file": "index.css",
70
- "useCase": "the ink centring for a token pill a badge, a KV value, any short caps-or-digits label in a fixed-height box; it needs no icon sibling (with one the box must stay flex, and the general trim applies).",
69
+ "useCase": "on a native single-line <input> (or the text layer that must mirror one) inside a fixed-height field never alongside era-text-trim.",
70
+ "description": "The EDITABLE counterpart of era-text-trim — for native <input>s, which the\ntrim is forbidden on (it collapses the UA's inner editor box and slices the\nglyphs; see typography/collapsed-text-trim).\n\nWithout help, a tier-height input centres its text by FONT BOX, and the ui\nstacks' font box is deeply asymmetric: ascent ≈1.07em against descent\n≈0.29em. Centring that box parks the visible band (caps, x-height) ~0.115em\nLOW — so a field's text sat visibly below the trimmed label beside it.\n\nThe value is OPTICAL, not the derivation's: pure x-band centring computes\nto ~0.23em of padding, but pixel-measured against real Title-Case text it\nover-lifts — the eye weighs the cap-to-baseline mass, which x-band\ncentring parks ~2px high (caps 2.75px off the top against 5.7px of air\nunder the baseline, measured at dense). 0.08em splits the difference\nbetween the x-band ideal and the cap-band ideal, landing caps ~3.7px in an\n18px field with ~4.7px under the baseline — the reading where neither\nband's partisans win and neither complains.\n\nThe lift is padding, not a translateY: an icon-less Input paints the field\nchrome itself, and a transform would drag the field along with the ink.\npadding-bottom shrinks the content box from below, and a single-line input\nre-centres its line box in what remains — the ink rises by exactly half the\npadding, the chrome doesn't move, and the caret and selection follow the\ntext. In em on purpose: the correction is a fraction of the type, so it\nholds across every density and text size with one value.",
71
+ "css": "@utility era-ink-center {\n\tpadding-bottom: 0.08em;\n}"
72
+ },
73
+ {
74
+ "name": "era-token-ink",
75
+ "names": [
76
+ "era-token-ink"
77
+ ],
78
+ "file": "index.css",
79
+ "useCase": "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.",
71
80
  "description": "TOKEN INK — the ink centring for a pill whose content is a TOKEN: an id, a\ncount, a status, a tag. Font-independent, unlike everything it replaces.\n\nTHE BUG IT FIXES, which had been shipping library-wide and invisibly:\n`text-box-trim` DOES NOTHING ON A FLEX CONTAINER. Era's controls are all\nflex, and a flex container's bare text becomes an ANONYMOUS flex item — a box\nno selector can reach and no non-inherited property can touch. So\n`era-text-trim` on a Badge, a Chip, a Button or a Bar contributed exactly one\nthing: `line-height: 1`. The ink was then centred by `align-items: center`\non the LINE BOX, which is precisely the font-metric-dependent centring the\ntrim exists to eliminate. Measured on the shipped mono stack: a badge reading\n\"AC-2.1\" sat 2px high in an 18px pill, and no amount of tuning the trim's\nBAND changed it, because the trim was never running.\n\nThe mechanism here restores it: a BLOCK box (where text-box-trim does apply)\nwhose trimmed line box is then centred by `align-content` — block-container\nalignment, so no flex formatting context and no anonymous item. The browser\ncomputes the band from the font's own metrics, which is what makes it hold\nacross ui-monospace, system-ui and ui-serif instead of needing a constant\nper stack.\n\nCAP band, not the x-height band the general trim uses, because the content\nclass is different: a token is caps and digits (both cap-height), and\ncentring the x-band parks them high by (cap − x)/2. Measured on the shipped\nstacks, cap-band centring takes an id/count/status from −2.0px to 0.0.\nProse and sentence-case labels keep era-text-trim; this is for tokens.",
72
- "css": "@utility era-token-ink {\n\tdisplay: block;\n\talign-content: center;\n\ttext-align: center;\n\tline-height: 1;\n\ttext-box: trim-both cap alphabetic;\n}\n\n@utility era-ink-center {\n\tpadding-bottom: 0.08em;\n}"
81
+ "css": "@utility era-token-ink {\n\tdisplay: block;\n\talign-content: center;\n\ttext-align: center;\n\tline-height: 1;\n\ttext-box: trim-both cap alphabetic;\n}"
82
+ },
83
+ {
84
+ "name": "era-label-ink",
85
+ "names": [
86
+ "era-label-ink"
87
+ ],
88
+ "file": "index.css",
89
+ "useCase": "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).",
90
+ "description": "LABEL INK — era-token-ink's mixed-case sibling, and the general answer to\n\"this fixed-height control holds one line of text\".\n\nSame mechanism, same reason: `text-box-trim` needs a BLOCK box, so the row\nbecomes one and `align-content` (block-container alignment) does the\ncentring. What differs is the band and the alignment, because the content\nclass differs — a label is sentence case, so the x-height band is the visual\nmass to centre (cap-band reads low on \"System\"), and it is laid out against\nthe row's padding rather than centred in it.\n\nWHY IT EXISTS AT ALL: era's controls are flex, and a flex container's bare\ntext is an ANONYMOUS flex item — a box no selector reaches and no\nnon-inherited property touches. So `era-text-trim` on a flex row is inert,\ncontributing only its `line-height: 1`, and the ink falls back to line-box\ncentring by font metrics. era-token-ink already fixed that for centred\ntokens; this fixes it for everything else.\n\nTHE ICON CASE IS NOT SOLVED, and cannot be from CSS: an icon beside the text\nneeds the box to stay flex, which re-creates the anonymous item. A component\nwhose children are an opaque slot therefore guards it —\n`not-has-[svg]:era-label-ink` — and ink-centres the text-only case while\nleaving the icon case on line-box centring. Splitting icon and label into\nseparate slots is what would close it; typography/inert-trim reports every\nsite still on the fallback.",
91
+ "css": "@utility era-label-ink {\n\tdisplay: block;\n\talign-content: center;\n\tline-height: 1;\n\ttext-box: trim-both ex alphabetic;\n}"
73
92
  },
74
93
  {
75
94
  "name": "era-shimmer",
@@ -9,7 +9,7 @@ never the `h-(--era-h-control)` var-class spelling. Everything below rides along
9
9
  with the stylesheet and needs no import:
10
10
 
11
11
  ```ts
12
- import "@sig-nine/era-ui/css";
12
+ import "@signal9/era-ui/css";
13
13
  ```
14
14
 
15
15
  ```svelte
@@ -44,7 +44,9 @@ enough to stay var-form — `min-w-(--era-h-control)` etc. remain the escape hat
44
44
  | `era-text-trim` | vertically center single-line control text (a label beside an icon, a button's own text) without a translateY nudge. Fixed-height, non-editable elements ONLY: never on an <input>/<textarea>, and never on the same element as `truncate` or any overflow clip — put it on the fixed-height ROW and let the truncating child centre inside it. See era-text-trim-caps for ALL-CAPS. |
45
45
  | `truncate` | single-line horizontal truncation with an ellipsis — put it on the truncating CHILD inside a trimmed or leading-none row, never on the same element as era-text-trim; it will not clip ascenders or descenders. |
46
46
  | `era-text-trim-caps` | the era-text-trim variant for ALL-CAPS single-line strings (kbd hints, acronym badges) — never for mixed-case text (a label like "System" reads high; use era-text-trim), and never on the same element as `truncate`. |
47
- | `era-token-ink` `era-ink-center` | the ink centring for a token pill a badge, a KV value, any short caps-or-digits label in a fixed-height box; it needs no icon sibling (with one the box must stay flex, and the general trim applies). |
47
+ | `era-ink-center` | on a native single-line <input> (or the text layer that must mirror one) inside a fixed-height field never alongside era-text-trim. |
48
+ | `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. |
49
+ | `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). |
48
50
  | `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. |
49
51
  | `era-collapse` | the height sweep for a bits-ui Collapsible.Content — put it on the Content element itself (it carries its own overflow: hidden). |
50
52
  | `h-icon` `h-pill` `h-chip` `h-control` `h-bar` | the tier-height ladder — h-icon (icon) · h-pill (nested pill) · h-chip (standalone pill) · h-control (control) · h-bar (bar); the named form of h-(--era-h-*). |
@@ -235,9 +237,47 @@ Declared in `index.css`.
235
237
  }
236
238
  ```
237
239
 
238
- ## era-token-ink · era-ink-center
240
+ ## era-ink-center
239
241
 
240
- **Use it for:** the ink centring for a token pill a badge, a KV value, any short caps-or-digits label in a fixed-height box; it needs no icon sibling (with one the box must stay flex, and the general trim applies).
242
+ **Use it for:** on a native single-line <input> (or the text layer that must mirror one) inside a fixed-height field never alongside era-text-trim.
243
+
244
+ The EDITABLE counterpart of era-text-trim — for native <input>s, which the
245
+ trim is forbidden on (it collapses the UA's inner editor box and slices the
246
+ glyphs; see typography/collapsed-text-trim).
247
+
248
+ Without help, a tier-height input centres its text by FONT BOX, and the ui
249
+ stacks' font box is deeply asymmetric: ascent ≈1.07em against descent
250
+ ≈0.29em. Centring that box parks the visible band (caps, x-height) ~0.115em
251
+ LOW — so a field's text sat visibly below the trimmed label beside it.
252
+
253
+ The value is OPTICAL, not the derivation's: pure x-band centring computes
254
+ to ~0.23em of padding, but pixel-measured against real Title-Case text it
255
+ over-lifts — the eye weighs the cap-to-baseline mass, which x-band
256
+ centring parks ~2px high (caps 2.75px off the top against 5.7px of air
257
+ under the baseline, measured at dense). 0.08em splits the difference
258
+ between the x-band ideal and the cap-band ideal, landing caps ~3.7px in an
259
+ 18px field with ~4.7px under the baseline — the reading where neither
260
+ band's partisans win and neither complains.
261
+
262
+ The lift is padding, not a translateY: an icon-less Input paints the field
263
+ chrome itself, and a transform would drag the field along with the ink.
264
+ padding-bottom shrinks the content box from below, and a single-line input
265
+ re-centres its line box in what remains — the ink rises by exactly half the
266
+ padding, the chrome doesn't move, and the caret and selection follow the
267
+ text. In em on purpose: the correction is a fraction of the type, so it
268
+ holds across every density and text size with one value.
269
+
270
+ Declared in `index.css`.
271
+
272
+ ```css
273
+ @utility era-ink-center {
274
+ padding-bottom: 0.08em;
275
+ }
276
+ ```
277
+
278
+ ## era-token-ink
279
+
280
+ **Use it for:** 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.
241
281
 
242
282
  TOKEN INK — the ink centring for a pill whose content is a TOKEN: an id, a
243
283
  count, a status, a tag. Font-independent, unlike everything it replaces.
@@ -276,9 +316,45 @@ Declared in `index.css`.
276
316
  line-height: 1;
277
317
  text-box: trim-both cap alphabetic;
278
318
  }
319
+ ```
279
320
 
280
- @utility era-ink-center {
281
- padding-bottom: 0.08em;
321
+ ## era-label-ink
322
+
323
+ **Use it for:** 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).
324
+
325
+ LABEL INK — era-token-ink's mixed-case sibling, and the general answer to
326
+ "this fixed-height control holds one line of text".
327
+
328
+ Same mechanism, same reason: `text-box-trim` needs a BLOCK box, so the row
329
+ becomes one and `align-content` (block-container alignment) does the
330
+ centring. What differs is the band and the alignment, because the content
331
+ class differs — a label is sentence case, so the x-height band is the visual
332
+ mass to centre (cap-band reads low on "System"), and it is laid out against
333
+ the row's padding rather than centred in it.
334
+
335
+ WHY IT EXISTS AT ALL: era's controls are flex, and a flex container's bare
336
+ text is an ANONYMOUS flex item — a box no selector reaches and no
337
+ non-inherited property touches. So `era-text-trim` on a flex row is inert,
338
+ contributing only its `line-height: 1`, and the ink falls back to line-box
339
+ centring by font metrics. era-token-ink already fixed that for centred
340
+ tokens; this fixes it for everything else.
341
+
342
+ THE ICON CASE IS NOT SOLVED, and cannot be from CSS: an icon beside the text
343
+ needs the box to stay flex, which re-creates the anonymous item. A component
344
+ whose children are an opaque slot therefore guards it —
345
+ `not-has-[svg]:era-label-ink` — and ink-centres the text-only case while
346
+ leaving the icon case on line-box centring. Splitting icon and label into
347
+ separate slots is what would close it; typography/inert-trim reports every
348
+ site still on the fallback.
349
+
350
+ Declared in `index.css`.
351
+
352
+ ```css
353
+ @utility era-label-ink {
354
+ display: block;
355
+ align-content: center;
356
+ line-height: 1;
357
+ text-box: trim-both ex alphabetic;
282
358
  }
283
359
  ```
284
360
 
@@ -227,7 +227,9 @@
227
227
  * the pair so the runtime audit can point at it). It exists so the failure mode
228
228
  * is a no-op instead of unreadable text. */
229
229
  .era-text-trim.truncate,
230
- .era-text-trim-caps.truncate {
230
+ .era-text-trim-caps.truncate,
231
+ .era-label-ink.truncate,
232
+ .era-token-ink.truncate {
231
233
  line-height: normal;
232
234
  text-box: normal;
233
235
  }
@@ -271,6 +273,10 @@
271
273
  *
272
274
  * @use on a native single-line <input> (or the text layer that must mirror
273
275
  * one) inside a fixed-height field — never alongside era-text-trim. */
276
+ @utility era-ink-center {
277
+ padding-bottom: 0.08em;
278
+ }
279
+
274
280
  /* TOKEN INK — the ink centring for a pill whose content is a TOKEN: an id, a
275
281
  * count, a status, a tag. Font-independent, unlike everything it replaces.
276
282
  *
@@ -299,8 +305,9 @@
299
305
  * Prose and sentence-case labels keep era-text-trim; this is for tokens.
300
306
  *
301
307
  * @use the ink centring for a token pill — a badge, a KV value, any short
302
- * caps-or-digits label in a fixed-height box; it needs no icon sibling (with
303
- * one the box must stay flex, and the general trim applies). */
308
+ * caps-or-digits label in a fixed-height box, CENTRED horizontally and with no
309
+ * icon sibling. For a left-aligned sentence-case label use era-label-ink; when
310
+ * an icon really must sit beside the text, see the note there. */
304
311
  @utility era-token-ink {
305
312
  display: block;
306
313
  align-content: center;
@@ -309,8 +316,39 @@
309
316
  text-box: trim-both cap alphabetic;
310
317
  }
311
318
 
312
- @utility era-ink-center {
313
- padding-bottom: 0.08em;
319
+ /* LABEL INK — era-token-ink's mixed-case sibling, and the general answer to
320
+ * "this fixed-height control holds one line of text".
321
+ *
322
+ * Same mechanism, same reason: `text-box-trim` needs a BLOCK box, so the row
323
+ * becomes one and `align-content` (block-container alignment) does the
324
+ * centring. What differs is the band and the alignment, because the content
325
+ * class differs — a label is sentence case, so the x-height band is the visual
326
+ * mass to centre (cap-band reads low on "System"), and it is laid out against
327
+ * the row's padding rather than centred in it.
328
+ *
329
+ * WHY IT EXISTS AT ALL: era's controls are flex, and a flex container's bare
330
+ * text is an ANONYMOUS flex item — a box no selector reaches and no
331
+ * non-inherited property touches. So `era-text-trim` on a flex row is inert,
332
+ * contributing only its `line-height: 1`, and the ink falls back to line-box
333
+ * centring by font metrics. era-token-ink already fixed that for centred
334
+ * tokens; this fixes it for everything else.
335
+ *
336
+ * THE ICON CASE IS NOT SOLVED, and cannot be from CSS: an icon beside the text
337
+ * needs the box to stay flex, which re-creates the anonymous item. A component
338
+ * whose children are an opaque slot therefore guards it —
339
+ * `not-has-[svg]:era-label-ink` — and ink-centres the text-only case while
340
+ * leaving the icon case on line-box centring. Splitting icon and label into
341
+ * separate slots is what would close it; typography/inert-trim reports every
342
+ * site still on the fallback.
343
+ *
344
+ * @use the ink centring for a fixed-height control holding ONE line of
345
+ * sentence-case text and no icon — a menu item, a nav trigger, a button label.
346
+ * Never alongside `truncate` (the trimmed box becomes the clip). */
347
+ @utility era-label-ink {
348
+ display: block;
349
+ align-content: center;
350
+ line-height: 1;
351
+ text-box: trim-both ex alphabetic;
314
352
  }
315
353
 
316
354
  /* In-progress text shimmer — a bright sweep through muted text (the standard
@@ -10,7 +10,7 @@
10
10
  // Radius stays concentric to the height; gap/px are sp/2 (xs-inset-sm),
11
11
  // the even gap for one-tier-smaller content at every size.
12
12
  export const barVariants = tv({
13
- base: 'flex items-center era-text-trim',
13
+ base: 'flex items-center leading-none',
14
14
  variants: {
15
15
  size: {
16
16
  chip: 'h-(--era-h-chip) rounded-(--era-rd-chip)',
@@ -44,7 +44,7 @@ export declare const barVariants: import("tailwind-variants").TVReturnType<{
44
44
  true: "shrink-0 rounded-t-none rounded-b-(--era-rd-control) [box-shadow:0_1px_0_var(--color-divider-faded)]";
45
45
  false: "";
46
46
  };
47
- }, undefined, "flex items-center era-text-trim", {
47
+ }, undefined, "flex items-center leading-none", {
48
48
  size: {
49
49
  chip: "h-(--era-h-chip) rounded-(--era-rd-chip)";
50
50
  control: "h-(--era-h-control) rounded-(--era-rd-control)";
@@ -70,7 +70,18 @@
70
70
  and they separated on press. Wrapping unifies them, and `gap: inherit`
71
71
  forwards the button's own gap so the icon/label spacing is unchanged.
72
72
  -->
73
- <span data-button-content class="inline-flex items-center justify-center [gap:inherit]">
73
+ <!--
74
+ not-has-[svg]:era-label-ink — a text-only button ink-centres its label.
75
+ The span is otherwise a flex box, and a flex box's bare text is an
76
+ ANONYMOUS item that `text-box-trim` can never reach, so the label fell back
77
+ to line-box centring by font metrics (the bug era-label-ink documents).
78
+ With an icon present the box must stay flex for the gap, and that case is
79
+ still on the fallback — typography/inert-trim reports it.
80
+ -->
81
+ <span
82
+ data-button-content
83
+ class="inline-flex items-center justify-center [gap:inherit] not-has-[svg]:era-label-ink"
84
+ >
74
85
  {#if loading}
75
86
  <LoaderCircle class="size-(--era-h-icon) shrink-0 animate-spin" aria-hidden="true" />
76
87
  {#if loadingLabel}<span class="sr-only">{loadingLabel}</span>{/if}