@ponchia/ui 0.7.0 → 0.8.1

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 (54) hide show
  1. package/CHANGELOG.md +178 -0
  2. package/README.md +3 -3
  3. package/classes/classes.json +79 -7
  4. package/classes/index.d.ts +53 -1
  5. package/classes/index.js +60 -0
  6. package/classes/vscode.css-custom-data.json +24 -0
  7. package/css/app.css +14 -3
  8. package/css/disclosure.css +7 -7
  9. package/css/feedback.css +11 -8
  10. package/css/forms.css +3 -3
  11. package/css/navigation.css +1 -1
  12. package/css/overlay.css +19 -5
  13. package/css/primitives.css +101 -6
  14. package/css/site.css +11 -5
  15. package/css/skins.css +97 -3
  16. package/css/state.css +161 -0
  17. package/css/table.css +1 -1
  18. package/css/tokens.css +6 -0
  19. package/css/workbench.css +151 -0
  20. package/dist/bronto.css +1 -1
  21. package/dist/css/app.css +1 -1
  22. package/dist/css/disclosure.css +1 -1
  23. package/dist/css/feedback.css +1 -1
  24. package/dist/css/forms.css +1 -1
  25. package/dist/css/navigation.css +1 -1
  26. package/dist/css/overlay.css +1 -1
  27. package/dist/css/primitives.css +1 -1
  28. package/dist/css/report-kit.css +1 -1
  29. package/dist/css/site.css +1 -1
  30. package/dist/css/skins.css +1 -1
  31. package/dist/css/state.css +1 -1
  32. package/dist/css/table.css +1 -1
  33. package/dist/css/tokens.css +1 -1
  34. package/dist/css/workbench.css +1 -1
  35. package/docs/adr/0001-color-system.md +32 -3
  36. package/docs/contrast.md +102 -18
  37. package/docs/migrations/0.7-to-0.8.md +216 -0
  38. package/docs/package-contract.md +1 -0
  39. package/docs/reference.md +52 -1
  40. package/docs/reporting.md +8 -8
  41. package/docs/stability.md +31 -2
  42. package/docs/state.md +51 -1
  43. package/docs/theming.md +18 -0
  44. package/docs/usage.md +62 -2
  45. package/docs/workbench.md +83 -4
  46. package/llms.txt +1 -1
  47. package/package.json +13 -12
  48. package/tokens/figma.variables.json +84 -0
  49. package/tokens/index.d.ts +2 -2
  50. package/tokens/index.js +23 -0
  51. package/tokens/index.json +12 -0
  52. package/tokens/resolved.json +6 -0
  53. package/tokens/skins.js +117 -7
  54. package/tokens/tokens.dtcg.json +7 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,184 @@
5
5
  |> `^0` / `*` wildcard does **not** protect you. See README → Versioning, and
6
6
  |> the deprecation policy in CONTRIBUTING.md.
7
7
 
8
+ ## 0.8.1 — 2026-08-11
9
+
10
+ A packaging fix, plus dependency hygiene. **No published CSS, token, class, or
11
+ type artifact changed** — `dist/`, `css/`, `tokens/` and `classes/` are byte-for-byte
12
+ identical to 0.8.0. If you are on 0.8.0 and do not read the migration guide from
13
+ the package, there is nothing here for you.
14
+
15
+ ### Fixed
16
+
17
+ - **`docs/migrations/0.7-to-0.8.md` was missing from the published package.**
18
+ 0.8.0's one breaking change is the colorway canvas re-point, `CHANGELOG.md`
19
+ and `MIGRATIONS.json` both point at that guide, and it was not in the tarball —
20
+ so a consumer following the pointer from an offline install got nothing. The
21
+ guide was written, linked, and gated by `check:doc-links`; it was simply never
22
+ added to `package.json`'s hand-maintained `files` array.
23
+ - **The gate that should have caught it was circular.** `check-pack.mjs` derived
24
+ its set of shipped docs *from* `files`, so it proved every listed doc ships and
25
+ could never prove a doc that exists is listed. It now asserts the other
26
+ direction for the two directories the published package itself references —
27
+ `docs/migrations/` (from `MIGRATIONS.json`) and `docs/adr/` (from
28
+ `docs/architecture.md`) — where a missing file costs a reader a dead pointer.
29
+ Other `docs/` pages stay opt-in: the package ships a curated subset by design.
30
+
31
+ ### Changed
32
+
33
+ - **Dependency sweep** — ten Dependabot PRs landed as one reviewed change. Every
34
+ advisory was **devDependency-scoped**; the package declares no runtime
35
+ dependencies and `npm audit --omit=dev` was already clean, so none of them
36
+ ever reached a consumer. `npm audit` now reports 0 across the whole tree
37
+ (`fast-uri`, `js-yaml`, `nanoid`, `pdfjs-dist`, `postcss`, `shell-quote`,
38
+ `tar`, `undici`), the toolchain moved (`jsdom` 30, `knip` 6.32, `prettier`
39
+ 3.9.6, `publint` 0.3.23, `react`/`react-dom` 19.2.8, `solid-js` 1.9.14,
40
+ `stylelint` 17.14.1, `vega` 6.3.1, `@arethetypeswrong/cli` 0.18.5), the pinned
41
+ action SHAs moved, and the Astro example moved to Astro 7 (verified building
42
+ against the packed 0.8.0 tarball).
43
+
44
+ ### Not taken
45
+
46
+ - **TypeScript 7.** Its default export is now `{ version, versionMajorMinor }` —
47
+ the compiler-API namespace moved in the native port — so `ts.ScriptTarget` is
48
+ `undefined` and `scripts/lib/import-policy.mjs` throws at `check:exports`.
49
+ Adapting this repository's AST tooling to that API is real work with its own
50
+ review, and there is no security driver: TypeScript is not in any advisory
51
+ here. Pinned to `^6.0.3`; Dependabot's dev-group PR stays open for it.
52
+
53
+ ## 0.8.0 — 2026-08-11
54
+
55
+ A single-consumer release. A full-surface audit of the largest downstream
56
+ consumer — a Yjs-collaborative spatial canvas workspace with roughly ten
57
+ thousand lines of its own CSS — found it using 26 of the 646 published classes
58
+ and hand-rebuilding much of the rest. Everything here comes from what that
59
+ consumer had to write because the framework did not provide it, or got wrong.
60
+
61
+ ### BREAKING
62
+
63
+ - **A colorway now re-points the neutral canvas, so every skinned surface
64
+ changes appearance.** No class, token, attribute, or export was removed or
65
+ renamed, and `bronto-ui-check` will report nothing — the break is *visual*,
66
+ which is why it is called out here rather than left in Changed. Until 0.7,
67
+ `data-bronto-skin` moved `--accent` and nothing else, and ADR-0001 step 4 said
68
+ so explicitly; a consumer could reasonably have relied on the canvas staying
69
+ neutral. From 0.8 the ten `SKIN_CANVAS_TOKENS` (`--bg`, `--bg-elevated`,
70
+ `--panel`, `--panel-strong`, `--panel-soft`, `--line`, `--line-strong`,
71
+ `--text`, `--text-soft`, `--text-dim`) are re-pointed per skin per theme.
72
+ - **If you want the old look**, re-declare those ten tokens after the skin
73
+ import; they are ordinary custom properties on `:root[data-bronto-skin=…]`
74
+ and un-layered app CSS wins.
75
+ - **If you already hand-wrote a canvas** for a skin — the case this change
76
+ exists to serve — delete it and check the result; yours was almost certainly
77
+ not contrast-gated, and this one is.
78
+ - **Contrast is not a regression risk.** Each neutral keeps the core token's
79
+ OKLCH lightness exactly, and `check-contrast` re-measures all 21 gated
80
+ pairings per skin per theme. Status colours are untouched by design.
81
+
82
+ ### Fixed
83
+
84
+ - **The coarse-pointer tap target was 43.5px, not 44px.** The floor was written
85
+ as a bare `2.9rem` at 23 sites across 8 stylesheets, and `css/base.css` sets
86
+ `html { font-size: 0.9375rem }` — so every control the framework floats on
87
+ touch landed half a pixel under WCAG 2.5.5 and both platform HIGs, and drifted
88
+ further under any host that shrank the root. Several comments asserted
89
+ "≈ 44px". Both floors are now published tokens clamped in px
90
+ (`--tap-target: max(44px, 2.9rem)`, `--tap-target-min: max(24px, 1.6rem)`) —
91
+ the same `max()` shape the 24px floor already used correctly in five places.
92
+ The e2e that should have caught this was derived from the token (`2.9 * rem`),
93
+ so it agreed with the bug; it now asserts the external 44px standard, and a
94
+ unit gate walks every `@media (pointer: coarse)` block and fails on any floor
95
+ written as a bare length. That gate immediately found one more:
96
+ `.ui-table__sort`.
97
+
98
+ ### Added
99
+
100
+ - **`.ui-button__label`** — the label slot for icon buttons. Wrap a button's
101
+ text in it and `--icon` decides whether the words are painted; they stay in
102
+ the accessible name and in text-based test selectors either way. One markup
103
+ shape serves both the labelled and icon-only forms, and no `aria-label` can
104
+ drift out of sync with the visible wording. The slot also ellipsises rather
105
+ than wrapping, so a labelled button in a tight bar shrinks instead of pushing
106
+ its neighbours out. `ui.button()` is unchanged; `cls.buttonLabel` is new.
107
+ - **`.ui-button--dense`** — a size tier for bars whose *height* is the
108
+ constraint (a pane title bar, a packed toolbar, a table row's actions). It
109
+ lowers only the visual floor, to `--tap-target-min`; the coarse-pointer block
110
+ still floats it to the full `--tap-target`, so a control shrunk for a mouse is
111
+ never shrunk for a finger. `ui.button({ size: 'dense' })`.
112
+ - **A canonical severity ladder** (`css/state.css`, opt-in). Bronto shipped the
113
+ *tones* long ago but never the *scale* — the tier names, their order, and the
114
+ attribute carrying them — so every consumer invented the ladder and it drifted
115
+ inside a single app: one surface saying `critical|error|warning|note`, the
116
+ next `bad|warn`, a third `critical|warning|info|ok`, under two different
117
+ attribute names, so findings did not sort against alerts. The ladder is
118
+ `critical` › `error` › `warning` › `notice` › `ok`, carried on **one**
119
+ attribute (`data-level`) across `.ui-severity`, `.ui-severity-dot` and
120
+ `.ui-severity-row`, with `SEVERITY_LEVELS` and `severity()` exported so a host
121
+ drives filters and sorts from the same list the CSS paints. `unknown` sits
122
+ deliberately *outside* the ordering: it means "not measured", and collapsing
123
+ it into `ok` is how a dead collector reads as a healthy system.
124
+ - **`.ui-pane`** (`css/workbench.css`, opt-in) — the window that `.ui-panel`
125
+ (a padded card) and `.ui-inspector` (head plus body) are not: a grab header,
126
+ a title that renames in place via `__title-input` without moving layout, and
127
+ an `__actions` slot that **scrolls rather than pushing** its last control past
128
+ the pane's clipped edge — the failure that leaves a Focus or Disconnect button
129
+ present, in the a11y tree, and unreachable.
130
+ - **`.ui-toolstrip--pane`** — the app has one toolstrip; a workbench full of
131
+ panes has one *per pane*, and those need different framing (no frame of their
132
+ own, a rule against the content below) and must not wrap, since a second row
133
+ would resize live content on every state change. `.ui-toolstrip__fill` marks
134
+ the element that absorbs slack and gives it back first.
135
+ - **`--anchored` / `--anchor-block-start` / `--anchor-block-end`** on
136
+ `.ui-selectionbar` and `.ui-toolstrip`. Both `--floating` bars were raised but
137
+ position-less, so every consumer re-derived the placement — including the
138
+ `max(offset, inset)` shape that keeps a bar out from under the home indicator.
139
+ - **Empty-state slots and an invite variant.** `.ui-empty-state` was a dashed
140
+ box that styled a `<p>`, so every empty surface in an app re-invented the same
141
+ three parts under a different name and they drifted. `__glyph` / `__lead` /
142
+ `__hint` name them, and `--invite` is the different job: an empty state
143
+ *reports absence*, an invite *offers the next action*, so it drops the dashed
144
+ frame and centres in the space it is given.
145
+ - **Safe-area tokens** — `--safe-area-top / -right / -bottom / -left`, defaulting
146
+ to `env(safe-area-inset-*, 0px)`. The framework had **no** `env()` awareness
147
+ while shipping eight viewport-anchored surfaces; all eight now read them: the
148
+ app rail and topbar, a sticky site header, the skip link, both toast stacks,
149
+ the drawer modal and the lightbox. Every rule uses
150
+ `max(<authored>, var(--safe-area-*))`, so desktop rendering is unchanged. The
151
+ values are indirected through custom properties rather than called at the
152
+ point of use because `env()` cannot be emulated by a desktop test runner —
153
+ and because a host inside its own chrome (embedded webview, kiosk frame) needs
154
+ to declare the real insets.
155
+
156
+ ### Changed
157
+
158
+ - **ADR-0001 step 4 amended** to permit the canvas re-point above (see
159
+ BREAKING). "Amber CRT" used to leave the surface grey, which is what drove the
160
+ consumer to hand-write an amber canvas in raw hex — dark theme only, outside
161
+ OKLCH, outside the contrast gate, with `e-ink` silently un-tinted. The canvas
162
+ is **derived rather than picked**: every neutral keeps the core token's OKLCH
163
+ *lightness* exactly and moves only hue and a small role-scaled chroma.
164
+ `check-skins` now rejects a partial canvas and a one-theme-only canvas, the
165
+ two shapes the hand-written version had.
166
+ - **`check:recipe-types` no longer mis-attributes options.** It sliced the
167
+ factory body to end-of-file, so the *last* recipe's chunk swallowed everything
168
+ declared after the `ui` object — an unrelated helper exported below it had its
169
+ string branches blamed on whichever recipe happened to be last. It now stops
170
+ at the object's own closing brace.
171
+ - `docs/stability.md` records the audit and the correction it forces on this
172
+ project's adoption model: "no inspected consumer imports the surface" has been
173
+ measuring **discoverability, not demand**. Those thirteen zero-use primitives
174
+ were never rejected; they were never found. Non-adoption of a leaf the
175
+ consumer never imported is not evidence for retiring it at 1.0.
176
+
177
+ ### Notes
178
+
179
+ - Anything that reads `tokens.dtcg.json` should know that six new scale tokens
180
+ are **deliberately absent** from it, listed in the root extension's
181
+ `omittedCssVariables`: the two tap-target floors are `max()` comparisons and
182
+ the four safe-area insets are `env()` reads, neither of which has a conforming
183
+ DTCG shape. `tokens.json` carries the authored CSS. This is the same treatment
184
+ `--shadow` and the em trackings already get.
185
+
8
186
  ## 0.7.0 — 2026-07-20
9
187
 
10
188
  A consumer-first contract-hardening release. Ten real applications and tools,
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![npm](https://img.shields.io/npm/v/@ponchia/ui?logo=npm)](https://www.npmjs.com/package/@ponchia/ui)
4
4
  [![npm provenance](https://img.shields.io/badge/npm-provenance-blue?logo=npm)](https://www.npmjs.com/package/@ponchia/ui#provenance)
5
5
  [![runtime deps](https://img.shields.io/badge/runtime%20deps-0-brightgreen)](https://github.com/Ponchia/bronto-ui/blob/main/package.json)
6
- [![default CSS](https://img.shields.io/badge/default%20CSS-~90kB%20%2F%20~15kB%20gzip-informational)](https://github.com/Ponchia/bronto-ui/blob/main/scripts/check-dist.mjs)
6
+ [![default CSS](https://img.shields.io/badge/default%20CSS-~92kB%20%2F%20~16kB%20gzip-informational)](https://github.com/Ponchia/bronto-ui/blob/main/scripts/check-dist.mjs)
7
7
  [![CI](https://github.com/Ponchia/bronto-ui/actions/workflows/ci.yml/badge.svg)](https://github.com/Ponchia/bronto-ui/actions/workflows/ci.yml)
8
8
  [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/Ponchia/bronto-ui/badge)](https://scorecard.dev/viewer/?uri=github.com/Ponchia/bronto-ui)
9
9
  [![license: MIT](https://img.shields.io/badge/license-MIT-blue)](https://github.com/Ponchia/bronto-ui/blob/main/LICENSE)
@@ -81,12 +81,12 @@ Or drop it in with no build step, straight from a CDN (replace the version only
81
81
  when deliberately upgrading across a breaking pre-1.0 minor):
82
82
 
83
83
  ```html
84
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ponchia/ui@0.7.0/dist/bronto.css">
84
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ponchia/ui@0.8.1/dist/bronto.css">
85
85
  ```
86
86
 
87
87
  ## Quick start
88
88
 
89
- **1. Load the CSS.** One flattened, minified default CSS bundle — the standard component set, one request (~90 kB raw / ~15 kB gzip) — that is `dist/bronto.css`, not the whole package tarball:
89
+ **1. Load the CSS.** One flattened, minified default CSS bundle — the standard component set, one request (~92 kB raw / ~16 kB gzip) — that is `dist/bronto.css`, not the whole package tarball:
90
90
 
91
91
  ```css
92
92
  @import '@ponchia/ui'; /* via a bundler */
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$comment": "@ponchia/ui class vocabulary as language-neutral data — validate emitted markup without executing the ESM cls map or parsing the .d.ts. Generated from classes/index.js — do not edit by hand; run `npm run classes:json:build`. Drift-checked in CI. `groups[].base` is null for a parts-only namespace (no standalone base class — do NOT emit it). A modifier whose name contains `__` (e.g. `ui-spark__bar--pos`) attaches to THAT part, not the base host. `states` is the author-applied `is-*` hooks (runtime/behavior-managed hooks are excluded); `behaviorAttributes` are the `data-bronto-*` wiring hooks the optional behaviors delegate on; `requiredAria` is the role/aria a generator must emit per component. `states` + `customProperties` are documented in docs/reference.md and ship outside `cls` by design.",
3
3
  "counts": {
4
- "classes": 644,
5
- "groups": 179
4
+ "classes": 669,
5
+ "groups": 183
6
6
  },
7
7
  "groups": {
8
8
  "ui-accordion": {
@@ -242,13 +242,16 @@
242
242
  "base": "ui-button",
243
243
  "modifiers": [
244
244
  "ui-button--danger",
245
+ "ui-button--dense",
245
246
  "ui-button--ghost",
246
247
  "ui-button--icon",
247
248
  "ui-button--lg",
248
249
  "ui-button--sm",
249
250
  "ui-button--subtle"
250
251
  ],
251
- "parts": []
252
+ "parts": [
253
+ "ui-button__label"
254
+ ]
252
255
  },
253
256
  "ui-card": {
254
257
  "base": "ui-card",
@@ -545,8 +548,14 @@
545
548
  },
546
549
  "ui-empty-state": {
547
550
  "base": "ui-empty-state",
548
- "modifiers": [],
549
- "parts": []
551
+ "modifiers": [
552
+ "ui-empty-state--invite"
553
+ ],
554
+ "parts": [
555
+ "ui-empty-state__glyph",
556
+ "ui-empty-state__hint",
557
+ "ui-empty-state__lead"
558
+ ]
550
559
  },
551
560
  "ui-error-summary": {
552
561
  "base": "ui-error-summary",
@@ -910,6 +919,17 @@
910
919
  "ui-pagination__item"
911
920
  ]
912
921
  },
922
+ "ui-pane": {
923
+ "base": "ui-pane",
924
+ "modifiers": [],
925
+ "parts": [
926
+ "ui-pane__actions",
927
+ "ui-pane__body",
928
+ "ui-pane__head",
929
+ "ui-pane__title",
930
+ "ui-pane__title-input"
931
+ ]
932
+ },
913
933
  "ui-panel": {
914
934
  "base": "ui-panel",
915
935
  "modifiers": [],
@@ -1104,12 +1124,34 @@
1104
1124
  },
1105
1125
  "ui-selectionbar": {
1106
1126
  "base": "ui-selectionbar",
1107
- "modifiers": [],
1127
+ "modifiers": [
1128
+ "ui-selectionbar--anchor-block-end",
1129
+ "ui-selectionbar--anchor-block-start",
1130
+ "ui-selectionbar--anchored"
1131
+ ],
1108
1132
  "parts": [
1109
1133
  "ui-selectionbar__actions",
1110
1134
  "ui-selectionbar__count"
1111
1135
  ]
1112
1136
  },
1137
+ "ui-severity": {
1138
+ "base": "ui-severity",
1139
+ "modifiers": [],
1140
+ "parts": []
1141
+ },
1142
+ "ui-severity-dot": {
1143
+ "base": "ui-severity-dot",
1144
+ "modifiers": [],
1145
+ "parts": []
1146
+ },
1147
+ "ui-severity-row": {
1148
+ "base": "ui-severity-row",
1149
+ "modifiers": [],
1150
+ "parts": [
1151
+ "ui-severity-row__meta",
1152
+ "ui-severity-row__title"
1153
+ ]
1154
+ },
1113
1155
  "ui-shortcut": {
1114
1156
  "base": "ui-shortcut",
1115
1157
  "modifiers": [],
@@ -1447,13 +1489,18 @@
1447
1489
  "ui-toolstrip": {
1448
1490
  "base": "ui-toolstrip",
1449
1491
  "modifiers": [
1492
+ "ui-toolstrip--anchor-block-end",
1493
+ "ui-toolstrip--anchor-block-start",
1494
+ "ui-toolstrip--anchored",
1450
1495
  "ui-toolstrip--compact",
1451
- "ui-toolstrip--floating"
1496
+ "ui-toolstrip--floating",
1497
+ "ui-toolstrip--pane"
1452
1498
  ],
1453
1499
  "parts": [
1454
1500
  "ui-toolstrip__actions",
1455
1501
  "ui-toolstrip__brand",
1456
1502
  "ui-toolstrip__context",
1503
+ "ui-toolstrip__fill",
1457
1504
  "ui-toolstrip__group",
1458
1505
  "ui-toolstrip__search"
1459
1506
  ]
@@ -1610,11 +1657,13 @@
1610
1657
  "ui-bullet__target",
1611
1658
  "ui-button",
1612
1659
  "ui-button--danger",
1660
+ "ui-button--dense",
1613
1661
  "ui-button--ghost",
1614
1662
  "ui-button--icon",
1615
1663
  "ui-button--lg",
1616
1664
  "ui-button--sm",
1617
1665
  "ui-button--subtle",
1666
+ "ui-button__label",
1618
1667
  "ui-card",
1619
1668
  "ui-card--accent",
1620
1669
  "ui-card--interactive",
@@ -1748,6 +1797,10 @@
1748
1797
  "ui-dotspinner--lg",
1749
1798
  "ui-dotspinner--sm",
1750
1799
  "ui-empty-state",
1800
+ "ui-empty-state--invite",
1801
+ "ui-empty-state__glyph",
1802
+ "ui-empty-state__hint",
1803
+ "ui-empty-state__lead",
1751
1804
  "ui-error-summary",
1752
1805
  "ui-error-summary__list",
1753
1806
  "ui-error-summary__title",
@@ -1907,6 +1960,12 @@
1907
1960
  "ui-pagehead__title",
1908
1961
  "ui-pagination",
1909
1962
  "ui-pagination__item",
1963
+ "ui-pane",
1964
+ "ui-pane__actions",
1965
+ "ui-pane__body",
1966
+ "ui-pane__head",
1967
+ "ui-pane__title",
1968
+ "ui-pane__title-input",
1910
1969
  "ui-panel",
1911
1970
  "ui-panel__head",
1912
1971
  "ui-popover",
@@ -1994,8 +2053,16 @@
1994
2053
  "ui-sel--on",
1995
2054
  "ui-select",
1996
2055
  "ui-selectionbar",
2056
+ "ui-selectionbar--anchor-block-end",
2057
+ "ui-selectionbar--anchor-block-start",
2058
+ "ui-selectionbar--anchored",
1997
2059
  "ui-selectionbar__actions",
1998
2060
  "ui-selectionbar__count",
2061
+ "ui-severity",
2062
+ "ui-severity-dot",
2063
+ "ui-severity-row",
2064
+ "ui-severity-row__meta",
2065
+ "ui-severity-row__title",
1999
2066
  "ui-shortcut",
2000
2067
  "ui-shortcut__sep",
2001
2068
  "ui-sidebar",
@@ -2124,11 +2191,16 @@
2124
2191
  "ui-tool-call__status",
2125
2192
  "ui-tool-log",
2126
2193
  "ui-toolstrip",
2194
+ "ui-toolstrip--anchor-block-end",
2195
+ "ui-toolstrip--anchor-block-start",
2196
+ "ui-toolstrip--anchored",
2127
2197
  "ui-toolstrip--compact",
2128
2198
  "ui-toolstrip--floating",
2199
+ "ui-toolstrip--pane",
2129
2200
  "ui-toolstrip__actions",
2130
2201
  "ui-toolstrip__brand",
2131
2202
  "ui-toolstrip__context",
2203
+ "ui-toolstrip__fill",
2132
2204
  "ui-toolstrip__group",
2133
2205
  "ui-toolstrip__search",
2134
2206
  "ui-tooltip",
@@ -21,6 +21,8 @@ export declare const cls: {
21
21
  readonly buttonIcon: 'ui-button--icon';
22
22
  readonly buttonSm: 'ui-button--sm';
23
23
  readonly buttonLg: 'ui-button--lg';
24
+ readonly buttonDense: 'ui-button--dense';
25
+ readonly buttonLabel: 'ui-button__label';
24
26
  readonly card: 'ui-card';
25
27
  readonly cardHead: 'ui-card__head';
26
28
  readonly cardAccent: 'ui-card--accent';
@@ -54,6 +56,10 @@ export declare const cls: {
54
56
  readonly linkCta: 'ui-link--cta';
55
57
  readonly keyValue: 'ui-key-value';
56
58
  readonly emptyState: 'ui-empty-state';
59
+ readonly emptyStateInvite: 'ui-empty-state--invite';
60
+ readonly emptyStateGlyph: 'ui-empty-state__glyph';
61
+ readonly emptyStateLead: 'ui-empty-state__lead';
62
+ readonly emptyStateHint: 'ui-empty-state__hint';
57
63
  readonly dot: 'ui-dot';
58
64
  readonly dotAccent: 'ui-dot--accent';
59
65
  readonly dotSuccess: 'ui-dot--success';
@@ -560,6 +566,11 @@ export declare const cls: {
560
566
  readonly jobBlocked: 'ui-job--blocked';
561
567
  readonly jobFailed: 'ui-job--failed';
562
568
  readonly jobComplete: 'ui-job--complete';
569
+ readonly severity: 'ui-severity';
570
+ readonly severityDot: 'ui-severity-dot';
571
+ readonly severityRow: 'ui-severity-row';
572
+ readonly severityRowTitle: 'ui-severity-row__title';
573
+ readonly severityRowMeta: 'ui-severity-row__meta';
563
574
  readonly generated: 'ui-generated';
564
575
  readonly generatedLabel: 'ui-generated__label';
565
576
  readonly originLabel: 'ui-origin-label';
@@ -577,11 +588,22 @@ export declare const cls: {
577
588
  readonly toolstrip: 'ui-toolstrip';
578
589
  readonly toolstripFloating: 'ui-toolstrip--floating';
579
590
  readonly toolstripCompact: 'ui-toolstrip--compact';
591
+ readonly toolstripPane: 'ui-toolstrip--pane';
592
+ readonly toolstripAnchored: 'ui-toolstrip--anchored';
593
+ readonly toolstripAnchorBlockStart: 'ui-toolstrip--anchor-block-start';
594
+ readonly toolstripAnchorBlockEnd: 'ui-toolstrip--anchor-block-end';
580
595
  readonly toolstripBrand: 'ui-toolstrip__brand';
581
596
  readonly toolstripContext: 'ui-toolstrip__context';
582
597
  readonly toolstripGroup: 'ui-toolstrip__group';
583
598
  readonly toolstripActions: 'ui-toolstrip__actions';
599
+ readonly toolstripFill: 'ui-toolstrip__fill';
584
600
  readonly toolstripSearch: 'ui-toolstrip__search';
601
+ readonly pane: 'ui-pane';
602
+ readonly paneHead: 'ui-pane__head';
603
+ readonly paneTitle: 'ui-pane__title';
604
+ readonly paneTitleInput: 'ui-pane__title-input';
605
+ readonly paneActions: 'ui-pane__actions';
606
+ readonly paneBody: 'ui-pane__body';
585
607
  readonly segmentedButtons: 'ui-segmented-buttons';
586
608
  readonly segmentedButtonsButton: 'ui-segmented-buttons__button';
587
609
  readonly property: 'ui-property';
@@ -590,6 +612,9 @@ export declare const cls: {
590
612
  readonly selectionbar: 'ui-selectionbar';
591
613
  readonly selectionbarCount: 'ui-selectionbar__count';
592
614
  readonly selectionbarActions: 'ui-selectionbar__actions';
615
+ readonly selectionbarAnchored: 'ui-selectionbar--anchored';
616
+ readonly selectionbarAnchorBlockStart: 'ui-selectionbar--anchor-block-start';
617
+ readonly selectionbarAnchorBlockEnd: 'ui-selectionbar--anchor-block-end';
593
618
  readonly splitter: 'ui-splitter';
594
619
  readonly splitterVertical: 'ui-splitter--vertical';
595
620
  readonly splitterHorizontal: 'ui-splitter--horizontal';
@@ -666,7 +691,17 @@ export declare function cx(...parts: readonly ClassValue[]): string;
666
691
  export interface ButtonOpts {
667
692
  variant?: 'ghost' | 'subtle' | 'danger';
668
693
  icon?: boolean;
669
- size?: 'sm' | 'lg';
694
+ size?: 'sm' | 'lg' | 'dense';
695
+ }
696
+ export interface EmptyStateOpts {
697
+ invite?: boolean;
698
+ }
699
+ export interface ToolstripOpts {
700
+ variant?: 'floating' | 'compact' | 'pane';
701
+ anchor?: 'block-start' | 'block-end';
702
+ }
703
+ export interface SelectionbarOpts {
704
+ anchor?: 'block-start' | 'block-end';
670
705
  }
671
706
  export interface CardOpts {
672
707
  accent?: boolean;
@@ -952,11 +987,28 @@ export interface Ui {
952
987
  state(opts?: StateOpts): string;
953
988
  job(opts?: JobOpts): string;
954
989
  originLabel(opts?: OriginLabelOpts): string;
990
+ emptyState(opts?: EmptyStateOpts): string;
991
+ toolstrip(opts?: ToolstripOpts): string;
992
+ selectionbar(opts?: SelectionbarOpts): string;
955
993
  }
956
994
 
957
995
  export declare const ui: Ui;
958
996
  export default ui;
959
997
 
998
+ /** The canonical severity ladder, worst to best. `unknown` is deliberately not
999
+ * a member: it means "not measured", not "nearly ok". */
1000
+ export declare const SEVERITY_LEVELS: readonly ['critical', 'error', 'warning', 'notice', 'ok'];
1001
+
1002
+ export type SeverityLevel = (typeof SEVERITY_LEVELS)[number];
1003
+
1004
+ /** Bundles `data-level` with the severity class so the attribute that carries
1005
+ * the meaning cannot be forgotten. An unrecognised level resolves to
1006
+ * `'unknown'`, which paints the neutral tone. */
1007
+ export declare function severity(
1008
+ level: SeverityLevel | (string & {}) | null | undefined,
1009
+ opts?: { part?: 'chip' | 'dot' | 'row' },
1010
+ ): { class: string; 'data-level': string };
1011
+
960
1012
  /** Min/max for the value-bearing fills; defaults to 0–100. */
961
1013
  export interface ValueRangeOpts {
962
1014
  min?: number;
package/classes/index.js CHANGED
@@ -25,6 +25,8 @@ export const cls = Object.freeze({
25
25
  buttonIcon: 'ui-button--icon',
26
26
  buttonSm: 'ui-button--sm',
27
27
  buttonLg: 'ui-button--lg',
28
+ buttonDense: 'ui-button--dense',
29
+ buttonLabel: 'ui-button__label',
28
30
  card: 'ui-card',
29
31
  cardHead: 'ui-card__head',
30
32
  cardAccent: 'ui-card--accent',
@@ -58,6 +60,10 @@ export const cls = Object.freeze({
58
60
  linkCta: 'ui-link--cta',
59
61
  keyValue: 'ui-key-value',
60
62
  emptyState: 'ui-empty-state',
63
+ emptyStateInvite: 'ui-empty-state--invite',
64
+ emptyStateGlyph: 'ui-empty-state__glyph',
65
+ emptyStateLead: 'ui-empty-state__lead',
66
+ emptyStateHint: 'ui-empty-state__hint',
61
67
  // dots
62
68
  dot: 'ui-dot',
63
69
  dotAccent: 'ui-dot--accent',
@@ -597,6 +603,12 @@ export const cls = Object.freeze({
597
603
  jobBlocked: 'ui-job--blocked',
598
604
  jobFailed: 'ui-job--failed',
599
605
  jobComplete: 'ui-job--complete',
606
+ // severity ladder — the second axis of state (css/state.css)
607
+ severity: 'ui-severity',
608
+ severityDot: 'ui-severity-dot',
609
+ severityRow: 'ui-severity-row',
610
+ severityRowTitle: 'ui-severity-row__title',
611
+ severityRowMeta: 'ui-severity-row__meta',
600
612
  // generated content / AI-trust surfaces (css/generated.css)
601
613
  generated: 'ui-generated',
602
614
  generatedLabel: 'ui-generated__label',
@@ -616,11 +628,22 @@ export const cls = Object.freeze({
616
628
  toolstrip: 'ui-toolstrip',
617
629
  toolstripFloating: 'ui-toolstrip--floating',
618
630
  toolstripCompact: 'ui-toolstrip--compact',
631
+ toolstripPane: 'ui-toolstrip--pane',
632
+ toolstripAnchored: 'ui-toolstrip--anchored',
633
+ toolstripAnchorBlockStart: 'ui-toolstrip--anchor-block-start',
634
+ toolstripAnchorBlockEnd: 'ui-toolstrip--anchor-block-end',
619
635
  toolstripBrand: 'ui-toolstrip__brand',
620
636
  toolstripContext: 'ui-toolstrip__context',
621
637
  toolstripGroup: 'ui-toolstrip__group',
622
638
  toolstripActions: 'ui-toolstrip__actions',
639
+ toolstripFill: 'ui-toolstrip__fill',
623
640
  toolstripSearch: 'ui-toolstrip__search',
641
+ pane: 'ui-pane',
642
+ paneHead: 'ui-pane__head',
643
+ paneTitle: 'ui-pane__title',
644
+ paneTitleInput: 'ui-pane__title-input',
645
+ paneActions: 'ui-pane__actions',
646
+ paneBody: 'ui-pane__body',
624
647
  segmentedButtons: 'ui-segmented-buttons',
625
648
  segmentedButtonsButton: 'ui-segmented-buttons__button',
626
649
  property: 'ui-property',
@@ -629,6 +652,9 @@ export const cls = Object.freeze({
629
652
  selectionbar: 'ui-selectionbar',
630
653
  selectionbarCount: 'ui-selectionbar__count',
631
654
  selectionbarActions: 'ui-selectionbar__actions',
655
+ selectionbarAnchored: 'ui-selectionbar--anchored',
656
+ selectionbarAnchorBlockStart: 'ui-selectionbar--anchor-block-start',
657
+ selectionbarAnchorBlockEnd: 'ui-selectionbar--anchor-block-end',
632
658
  splitter: 'ui-splitter',
633
659
  splitterVertical: 'ui-splitter--vertical',
634
660
  splitterHorizontal: 'ui-splitter--horizontal',
@@ -934,6 +960,7 @@ export const ui = {
934
960
  icon && cls.buttonIcon,
935
961
  size === 'sm' && cls.buttonSm,
936
962
  size === 'lg' && cls.buttonLg,
963
+ size === 'dense' && cls.buttonDense,
937
964
  ),
938
965
  card: ({ accent, interactive } = {}) =>
939
966
  j(cls.card, accent && cls.cardAccent, interactive && cls.cardInteractive),
@@ -1092,8 +1119,41 @@ export const ui = {
1092
1119
  state: ({ state, busy } = {}) => j(cls.state, stateTone(state), busy && cls.stateBusy),
1093
1120
  job: ({ state, compact } = {}) => j(cls.job, jobTone(state), compact && cls.jobCompact),
1094
1121
  originLabel: ({ ai } = {}) => j(cls.originLabel, ai && cls.originLabelAi),
1122
+ emptyState: ({ invite } = {}) => j(cls.emptyState, invite && cls.emptyStateInvite),
1123
+ toolstrip: ({ variant, anchor } = {}) =>
1124
+ j(
1125
+ cls.toolstrip,
1126
+ variant === 'floating' && cls.toolstripFloating,
1127
+ variant === 'compact' && cls.toolstripCompact,
1128
+ variant === 'pane' && cls.toolstripPane,
1129
+ anchor && cls.toolstripAnchored,
1130
+ anchor === 'block-start' && cls.toolstripAnchorBlockStart,
1131
+ anchor === 'block-end' && cls.toolstripAnchorBlockEnd,
1132
+ ),
1133
+ selectionbar: ({ anchor } = {}) =>
1134
+ j(
1135
+ cls.selectionbar,
1136
+ anchor && cls.selectionbarAnchored,
1137
+ anchor === 'block-start' && cls.selectionbarAnchorBlockStart,
1138
+ anchor === 'block-end' && cls.selectionbarAnchorBlockEnd,
1139
+ ),
1095
1140
  };
1096
1141
 
1142
+ /** The canonical severity ladder, worst to best. `unknown` is deliberately NOT
1143
+ * in the ordering — it means "not measured", not "nearly ok", and sorting it
1144
+ * next to `ok` is how a dead collector reads as a healthy system. Exported as
1145
+ * data so a host can drive filters and sorts from the same list the CSS
1146
+ * paints, instead of re-typing the tiers. */
1147
+ export const SEVERITY_LEVELS = Object.freeze(['critical', 'error', 'warning', 'notice', 'ok']);
1148
+
1149
+ /** `data-level` + the severity class, bundled so the attribute that carries the
1150
+ * meaning cannot be forgotten — the class alone paints the neutral tone and
1151
+ * silently loses the level. Unknown/absent levels resolve to `unknown`. */
1152
+ export const severity = (level, { part = 'chip' } = {}) => ({
1153
+ class: part === 'dot' ? cls.severityDot : part === 'row' ? cls.severityRow : cls.severity,
1154
+ 'data-level': SEVERITY_LEVELS.includes(level) ? level : 'unknown',
1155
+ });
1156
+
1097
1157
  // Attribute + style bundle for the data-bearing fills (`ui-meter`/`ui-progress`).
1098
1158
  // The class string alone paints a 0-width, unannounced bar: the fill width is a
1099
1159
  // UNITLESS `--value` (0–100) and AT needs role + aria-valuenow/min/max. This sets
@@ -257,6 +257,22 @@
257
257
  "name": "--radius-xl",
258
258
  "description": "Global scale token. Value: `4px`"
259
259
  },
260
+ {
261
+ "name": "--safe-area-bottom",
262
+ "description": "Global scale token. Value: `env(safe-area-inset-bottom, 0px)`"
263
+ },
264
+ {
265
+ "name": "--safe-area-left",
266
+ "description": "Global scale token. Value: `env(safe-area-inset-left, 0px)`"
267
+ },
268
+ {
269
+ "name": "--safe-area-right",
270
+ "description": "Global scale token. Value: `env(safe-area-inset-right, 0px)`"
271
+ },
272
+ {
273
+ "name": "--safe-area-top",
274
+ "description": "Global scale token. Value: `env(safe-area-inset-top, 0px)`"
275
+ },
260
276
  {
261
277
  "name": "--sans",
262
278
  "description": "Global scale token. Value: `'Inter', 'SF Pro Text', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif`"
@@ -341,6 +357,14 @@
341
357
  "name": "--surface-raised",
342
358
  "description": "Global scale token. Value: `var(--panel-strong)`"
343
359
  },
360
+ {
361
+ "name": "--tap-target",
362
+ "description": "Global scale token. Value: `max(44px, 2.9rem)`"
363
+ },
364
+ {
365
+ "name": "--tap-target-min",
366
+ "description": "Global scale token. Value: `max(24px, 1.6rem)`"
367
+ },
344
368
  {
345
369
  "name": "--text",
346
370
  "description": "Theme token. Light: `#0a0a0a` · Dark: `#e6e6e6`"