@ponchia/ui 0.6.12 → 0.8.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 (84) hide show
  1. package/CHANGELOG.md +210 -0
  2. package/MIGRATIONS.json +14 -0
  3. package/README.md +14 -6
  4. package/behaviors/dialog.d.ts.map +1 -1
  5. package/behaviors/dialog.js +14 -0
  6. package/behaviors/modal.d.ts +4 -0
  7. package/behaviors/modal.d.ts.map +1 -1
  8. package/behaviors/modal.js +4 -0
  9. package/behaviors/splitter.d.ts +2 -0
  10. package/behaviors/splitter.d.ts.map +1 -1
  11. package/behaviors/splitter.js +15 -1
  12. package/behaviors/theme.d.ts +3 -2
  13. package/behaviors/theme.d.ts.map +1 -1
  14. package/behaviors/theme.js +10 -6
  15. package/bin/bronto-ui-check.mjs +286 -0
  16. package/classes/classes.json +86 -7
  17. package/classes/index.d.ts +53 -1
  18. package/classes/index.js +60 -0
  19. package/classes/vscode.css-custom-data.json +24 -0
  20. package/css/app.css +14 -3
  21. package/css/disclosure.css +15 -5
  22. package/css/dots.css +43 -18
  23. package/css/feedback.css +46 -8
  24. package/css/forms.css +3 -3
  25. package/css/navigation.css +1 -1
  26. package/css/overlay.css +19 -5
  27. package/css/primitives.css +101 -6
  28. package/css/report.css +0 -40
  29. package/css/site.css +19 -3
  30. package/css/skins.css +97 -3
  31. package/css/state.css +161 -0
  32. package/css/table.css +1 -1
  33. package/css/tokens.css +6 -0
  34. package/css/workbench.css +151 -0
  35. package/dist/bronto.css +1 -1
  36. package/dist/css/app.css +1 -1
  37. package/dist/css/disclosure.css +1 -1
  38. package/dist/css/dots.css +1 -1
  39. package/dist/css/feedback.css +1 -1
  40. package/dist/css/forms.css +1 -1
  41. package/dist/css/navigation.css +1 -1
  42. package/dist/css/overlay.css +1 -1
  43. package/dist/css/primitives.css +1 -1
  44. package/dist/css/report-kit.css +1 -1
  45. package/dist/css/report.css +1 -1
  46. package/dist/css/site.css +1 -1
  47. package/dist/css/skins.css +1 -1
  48. package/dist/css/state.css +1 -1
  49. package/dist/css/table.css +1 -1
  50. package/dist/css/tokens.css +1 -1
  51. package/dist/css/workbench.css +1 -1
  52. package/docs/adr/0001-color-system.md +32 -3
  53. package/docs/adr/0004-prune-unused-adapters.md +34 -0
  54. package/docs/architecture.md +14 -10
  55. package/docs/command.md +18 -4
  56. package/docs/contrast.md +102 -18
  57. package/docs/migrations/0.6-to-0.7.md +85 -0
  58. package/docs/package-contract.md +10 -5
  59. package/docs/reference.md +52 -1
  60. package/docs/reporting.md +8 -8
  61. package/docs/stability.md +39 -9
  62. package/docs/state.md +51 -1
  63. package/docs/theming.md +67 -5
  64. package/docs/usage.md +119 -19
  65. package/docs/workbench.md +99 -6
  66. package/llms.txt +7 -3
  67. package/package.json +13 -3
  68. package/qwik/index.d.ts.map +1 -1
  69. package/qwik/index.js +4 -0
  70. package/react/index.d.ts.map +1 -1
  71. package/react/index.js +4 -0
  72. package/solid/index.d.ts.map +1 -1
  73. package/solid/index.js +4 -0
  74. package/svelte/index.d.ts.map +1 -1
  75. package/svelte/index.js +4 -0
  76. package/tokens/figma.variables.json +84 -0
  77. package/tokens/index.d.ts +2 -2
  78. package/tokens/index.js +23 -0
  79. package/tokens/index.json +12 -0
  80. package/tokens/resolved.json +6 -0
  81. package/tokens/skins.js +117 -7
  82. package/tokens/tokens.dtcg.json +2514 -399
  83. package/vue/index.d.ts.map +1 -1
  84. package/vue/index.js +4 -0
package/CHANGELOG.md CHANGED
@@ -5,6 +5,216 @@
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.0 — 2026-08-11
9
+
10
+ A single-consumer release. A full-surface audit of the largest downstream
11
+ consumer — a Yjs-collaborative spatial canvas workspace with roughly ten
12
+ thousand lines of its own CSS — found it using 26 of the 646 published classes
13
+ and hand-rebuilding much of the rest. Everything here comes from what that
14
+ consumer had to write because the framework did not provide it, or got wrong.
15
+
16
+ ### BREAKING
17
+
18
+ - **A colorway now re-points the neutral canvas, so every skinned surface
19
+ changes appearance.** No class, token, attribute, or export was removed or
20
+ renamed, and `bronto-ui-check` will report nothing — the break is *visual*,
21
+ which is why it is called out here rather than left in Changed. Until 0.7,
22
+ `data-bronto-skin` moved `--accent` and nothing else, and ADR-0001 step 4 said
23
+ so explicitly; a consumer could reasonably have relied on the canvas staying
24
+ neutral. From 0.8 the ten `SKIN_CANVAS_TOKENS` (`--bg`, `--bg-elevated`,
25
+ `--panel`, `--panel-strong`, `--panel-soft`, `--line`, `--line-strong`,
26
+ `--text`, `--text-soft`, `--text-dim`) are re-pointed per skin per theme.
27
+ - **If you want the old look**, re-declare those ten tokens after the skin
28
+ import; they are ordinary custom properties on `:root[data-bronto-skin=…]`
29
+ and un-layered app CSS wins.
30
+ - **If you already hand-wrote a canvas** for a skin — the case this change
31
+ exists to serve — delete it and check the result; yours was almost certainly
32
+ not contrast-gated, and this one is.
33
+ - **Contrast is not a regression risk.** Each neutral keeps the core token's
34
+ OKLCH lightness exactly, and `check-contrast` re-measures all 21 gated
35
+ pairings per skin per theme. Status colours are untouched by design.
36
+
37
+ ### Fixed
38
+
39
+ - **The coarse-pointer tap target was 43.5px, not 44px.** The floor was written
40
+ as a bare `2.9rem` at 23 sites across 8 stylesheets, and `css/base.css` sets
41
+ `html { font-size: 0.9375rem }` — so every control the framework floats on
42
+ touch landed half a pixel under WCAG 2.5.5 and both platform HIGs, and drifted
43
+ further under any host that shrank the root. Several comments asserted
44
+ "≈ 44px". Both floors are now published tokens clamped in px
45
+ (`--tap-target: max(44px, 2.9rem)`, `--tap-target-min: max(24px, 1.6rem)`) —
46
+ the same `max()` shape the 24px floor already used correctly in five places.
47
+ The e2e that should have caught this was derived from the token (`2.9 * rem`),
48
+ so it agreed with the bug; it now asserts the external 44px standard, and a
49
+ unit gate walks every `@media (pointer: coarse)` block and fails on any floor
50
+ written as a bare length. That gate immediately found one more:
51
+ `.ui-table__sort`.
52
+
53
+ ### Added
54
+
55
+ - **`.ui-button__label`** — the label slot for icon buttons. Wrap a button's
56
+ text in it and `--icon` decides whether the words are painted; they stay in
57
+ the accessible name and in text-based test selectors either way. One markup
58
+ shape serves both the labelled and icon-only forms, and no `aria-label` can
59
+ drift out of sync with the visible wording. The slot also ellipsises rather
60
+ than wrapping, so a labelled button in a tight bar shrinks instead of pushing
61
+ its neighbours out. `ui.button()` is unchanged; `cls.buttonLabel` is new.
62
+ - **`.ui-button--dense`** — a size tier for bars whose *height* is the
63
+ constraint (a pane title bar, a packed toolbar, a table row's actions). It
64
+ lowers only the visual floor, to `--tap-target-min`; the coarse-pointer block
65
+ still floats it to the full `--tap-target`, so a control shrunk for a mouse is
66
+ never shrunk for a finger. `ui.button({ size: 'dense' })`.
67
+ - **A canonical severity ladder** (`css/state.css`, opt-in). Bronto shipped the
68
+ *tones* long ago but never the *scale* — the tier names, their order, and the
69
+ attribute carrying them — so every consumer invented the ladder and it drifted
70
+ inside a single app: one surface saying `critical|error|warning|note`, the
71
+ next `bad|warn`, a third `critical|warning|info|ok`, under two different
72
+ attribute names, so findings did not sort against alerts. The ladder is
73
+ `critical` › `error` › `warning` › `notice` › `ok`, carried on **one**
74
+ attribute (`data-level`) across `.ui-severity`, `.ui-severity-dot` and
75
+ `.ui-severity-row`, with `SEVERITY_LEVELS` and `severity()` exported so a host
76
+ drives filters and sorts from the same list the CSS paints. `unknown` sits
77
+ deliberately *outside* the ordering: it means "not measured", and collapsing
78
+ it into `ok` is how a dead collector reads as a healthy system.
79
+ - **`.ui-pane`** (`css/workbench.css`, opt-in) — the window that `.ui-panel`
80
+ (a padded card) and `.ui-inspector` (head plus body) are not: a grab header,
81
+ a title that renames in place via `__title-input` without moving layout, and
82
+ an `__actions` slot that **scrolls rather than pushing** its last control past
83
+ the pane's clipped edge — the failure that leaves a Focus or Disconnect button
84
+ present, in the a11y tree, and unreachable.
85
+ - **`.ui-toolstrip--pane`** — the app has one toolstrip; a workbench full of
86
+ panes has one *per pane*, and those need different framing (no frame of their
87
+ own, a rule against the content below) and must not wrap, since a second row
88
+ would resize live content on every state change. `.ui-toolstrip__fill` marks
89
+ the element that absorbs slack and gives it back first.
90
+ - **`--anchored` / `--anchor-block-start` / `--anchor-block-end`** on
91
+ `.ui-selectionbar` and `.ui-toolstrip`. Both `--floating` bars were raised but
92
+ position-less, so every consumer re-derived the placement — including the
93
+ `max(offset, inset)` shape that keeps a bar out from under the home indicator.
94
+ - **Empty-state slots and an invite variant.** `.ui-empty-state` was a dashed
95
+ box that styled a `<p>`, so every empty surface in an app re-invented the same
96
+ three parts under a different name and they drifted. `__glyph` / `__lead` /
97
+ `__hint` name them, and `--invite` is the different job: an empty state
98
+ *reports absence*, an invite *offers the next action*, so it drops the dashed
99
+ frame and centres in the space it is given.
100
+ - **Safe-area tokens** — `--safe-area-top / -right / -bottom / -left`, defaulting
101
+ to `env(safe-area-inset-*, 0px)`. The framework had **no** `env()` awareness
102
+ while shipping eight viewport-anchored surfaces; all eight now read them: the
103
+ app rail and topbar, a sticky site header, the skip link, both toast stacks,
104
+ the drawer modal and the lightbox. Every rule uses
105
+ `max(<authored>, var(--safe-area-*))`, so desktop rendering is unchanged. The
106
+ values are indirected through custom properties rather than called at the
107
+ point of use because `env()` cannot be emulated by a desktop test runner —
108
+ and because a host inside its own chrome (embedded webview, kiosk frame) needs
109
+ to declare the real insets.
110
+
111
+ ### Changed
112
+
113
+ - **ADR-0001 step 4 amended** to permit the canvas re-point above (see
114
+ BREAKING). "Amber CRT" used to leave the surface grey, which is what drove the
115
+ consumer to hand-write an amber canvas in raw hex — dark theme only, outside
116
+ OKLCH, outside the contrast gate, with `e-ink` silently un-tinted. The canvas
117
+ is **derived rather than picked**: every neutral keeps the core token's OKLCH
118
+ *lightness* exactly and moves only hue and a small role-scaled chroma.
119
+ `check-skins` now rejects a partial canvas and a one-theme-only canvas, the
120
+ two shapes the hand-written version had.
121
+ - **`check:recipe-types` no longer mis-attributes options.** It sliced the
122
+ factory body to end-of-file, so the *last* recipe's chunk swallowed everything
123
+ declared after the `ui` object — an unrelated helper exported below it had its
124
+ string branches blamed on whichever recipe happened to be last. It now stops
125
+ at the object's own closing brace.
126
+ - `docs/stability.md` records the audit and the correction it forces on this
127
+ project's adoption model: "no inspected consumer imports the surface" has been
128
+ measuring **discoverability, not demand**. Those thirteen zero-use primitives
129
+ were never rejected; they were never found. Non-adoption of a leaf the
130
+ consumer never imported is not evidence for retiring it at 1.0.
131
+
132
+ ### Notes
133
+
134
+ - Anything that reads `tokens.dtcg.json` should know that six new scale tokens
135
+ are **deliberately absent** from it, listed in the root extension's
136
+ `omittedCssVariables`: the two tap-target floors are `max()` comparisons and
137
+ the four safe-area insets are `env()` reads, neither of which has a conforming
138
+ DTCG shape. `tokens.json` carries the authored CSS. This is the same treatment
139
+ `--shadow` and the em trackings already get.
140
+
141
+ ## 0.7.0 — 2026-07-20
142
+
143
+ A consumer-first contract-hardening release. Ten real applications and tools,
144
+ plus a web-platform and design-system landscape review, found more value in
145
+ repairing and pruning the existing surface than in adding components.
146
+
147
+ ### BREAKING
148
+
149
+ - **DTCG 2025.10 values.** `tokens.dtcg.json` now emits portable structured
150
+ color, dimension, duration, numeric, and cubic-bezier values. It no longer
151
+ emits CSS strings for typed values or `$value: null` placeholders. Derived
152
+ colors are resolved per light/dark theme; the original CSS expression remains
153
+ under `$extensions["com.ponchia.css"].authoredValue`. CSS-only shadow
154
+ expressions and em-based letter-spacing remain in `tokens.json` rather than
155
+ pretending to be portable DTCG values; the DTCG root extension lists those
156
+ deliberate omissions. Update JSON readers using
157
+ [`docs/migrations/0.6-to-0.7.md`](docs/migrations/0.6-to-0.7.md).
158
+
159
+ ### Added
160
+
161
+ - **Consumer contract checker.** The zero-dependency `bronto-ui-check` binary
162
+ scans consumer source for literal `ui-*` classes absent from `classes.json`
163
+ and unresolved Bronto-like `var(--*)` references. It understands local token
164
+ definitions, strips source comments, ignores Markdown prose and build/vendor
165
+ directories, supports explicit allowlists, and can emit JSON.
166
+ - **Non-drag splitter controls.** Buttons inside a splitter can use
167
+ `data-bronto-splitter-adjust="-10"` / `"10"` to change the first pane by a
168
+ signed percentage-point delta. This gives pointer users the same resize
169
+ function without requiring a dragging gesture.
170
+ - **DTCG semantic gate.** `check:dtcg` validates every emitted typed value and
171
+ rejects null placeholders or malformed structured values before publication.
172
+
173
+ ### Changed
174
+
175
+ - **Meter ownership.** The already-public `ui-meter__row`, `__label`, and
176
+ `__value` styling moves from optional `report.css` into core `feedback.css`.
177
+ Core consumers now receive the layout the public class contract promised;
178
+ report-kit output remains visually unchanged. Together with the explicit
179
+ 24×24px coarse-pointer utility-link floors below, this intentionally adds
180
+ 1,121 B raw / 164 B gzip to the default bundle versus 0.6.12 (now 89.9 kB raw /
181
+ 15.4 kB gzip) and raises the hard budget only enough to admit those contracts.
182
+ - **Release evidence.** CI and release documentation now treat real-consumer
183
+ literal validation, packed-tarball upgrades, and payload reporting as the
184
+ evidence for 1.0 readiness.
185
+
186
+ ### Fixed
187
+
188
+ - **Live dot composition.** `ui-dot--live` now describes motion only. A
189
+ standalone live dot defaults to success, while an explicit accent, success,
190
+ warning, danger, or info tone controls both the dot and its pulse ring,
191
+ including forced-colors mode.
192
+ - **OS theme synchronization.** An OS `prefers-color-scheme` change now emits
193
+ the existing `bronto:themechange` event when no explicit theme is set, keeping
194
+ consumer-rendered charts, icons, and labels synchronized with the CSS theme.
195
+ - **Named dialogs.** `initDialog()` now warns once when an opened native dialog
196
+ has no `aria-label`, `aria-labelledby`, or `title`. All shipped framework
197
+ examples provide a name.
198
+ - **Touch target floors.** Breadcrumb and footer utility links reach the WCAG
199
+ 2.5.8 24 CSS-pixel floor under a coarse pointer.
200
+
201
+ ### Deprecated
202
+
203
+ - **Framework adapter subpaths.** `@ponchia/ui/react`, `/solid`, `/qwik`,
204
+ `/svelte`, and `/vue` remain compatible in 0.7 but are scheduled for removal
205
+ no earlier than 0.8. None of the ten inspected consumers imports them; use the
206
+ vanilla behavior initializers in each framework's mount/cleanup lifecycle.
207
+ - **Controlled non-native modal.** `initModal()`, its adapter bindings, and
208
+ `data-bronto-modal` remain compatible in 0.7 but are scheduled for removal no
209
+ earlier than 0.8. Prefer native `<dialog>` with `initDialog()`.
210
+
211
+ ### Verified
212
+
213
+ - Unit, type, generated-artifact, package, schema, DTCG, class/token contract,
214
+ browser, accessibility, packed-example, and real-consumer checks are release
215
+ gates. The release evidence records consumer classes and imported surfaces
216
+ without exposing private project details.
217
+
8
218
  ## 0.6.12 — 2026-07-10
9
219
 
10
220
  A stabilization patch. It changes no public class, token name, behavior name,
package/MIGRATIONS.json CHANGED
@@ -101,6 +101,20 @@
101
101
  }
102
102
  ],
103
103
  "codemod": "No codemod — the .ui-chart* renderer has no replacement class. rg -l 'ui-chart\\\\b|ui-chart__' to find call sites, then rebuild each as a Vega-Lite chart (@ponchia/ui/vega) or a token-themed inline <svg>. See docs/vega.md."
104
+ },
105
+ {
106
+ "from": "0.6",
107
+ "to": "0.7",
108
+ "summary": "Contract-hardening release. tokens.dtcg.json now uses conforming DTCG 2025.10 structured values instead of CSS strings/null placeholders. Splitters gain an additive non-drag pointer path. Framework adapters and initModal are deprecated, but remain compatible throughout 0.7.",
109
+ "safe": [],
110
+ "manual": [
111
+ {
112
+ "old": "tokens.dtcg.json scalar color/dimension values and $value:null CSS-expression placeholders",
113
+ "new": "DTCG 2025.10 structured typed values resolved per light/dark theme",
114
+ "note": "Update tooling to read color objects ({ colorSpace, components, alpha, optional hex }) and dimension objects ({ value, unit }). Read tokens.json when the authored CSS var()/color-mix() expression is required. CSS-only shadow expressions are not part of the portable DTCG projection."
115
+ }
116
+ ],
117
+ "codemod": "No source codemod. Update the DTCG reader at its JSON access boundary, then run npx --no-install bronto-ui-check against consumer source. See docs/migrations/0.6-to-0.7.md."
104
118
  }
105
119
  ]
106
120
  }
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-~89kB%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)
@@ -77,15 +77,16 @@ for the thesis.
77
77
  npm i @ponchia/ui
78
78
  ```
79
79
 
80
- Or drop it in with no build step, straight from a CDN:
80
+ Or drop it in with no build step, straight from a CDN (replace the version only
81
+ when deliberately upgrading across a breaking pre-1.0 minor):
81
82
 
82
83
  ```html
83
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ponchia/ui@0.6.12/dist/bronto.css">
84
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ponchia/ui@0.8.0/dist/bronto.css">
84
85
  ```
85
86
 
86
87
  ## Quick start
87
88
 
88
- **1. Load the CSS.** One flattened, minified default CSS bundle — the standard component set, one request (~89 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:
89
90
 
90
91
  ```css
91
92
  @import '@ponchia/ui'; /* via a bundler */
@@ -187,7 +188,13 @@ Not an afterthought — a gate. Every contractual token pairing has a declared W
187
188
 
188
189
  ## Works with anything
189
190
 
190
- The CSS is the framework, so it works with React, Svelte/SvelteKit, Astro, Vue, Solid, Qwik or plain HTML — there's no component runtime to adopt. The optional `classes` and `behaviors` entrypoints pull in **no** UI framework and are SSR-safe. For React, Solid and Qwik there are also **optional thin bindings** — `@ponchia/ui/react`, `@ponchia/ui/solid` and `@ponchia/ui/qwik` wrap the behaviors as hooks (`useDialog`, `useToast`, …); `react`/`solid-js`/`@builder.io/qwik` are optional peer deps. Svelte and Vue get dependency-free lifecycle adapters too: `@ponchia/ui/svelte` exports actions, and `@ponchia/ui/vue` exports directives/plugin helpers over the same behavior layer.
191
+ The CSS is the framework, so it works with React, Svelte/SvelteKit, Astro, Vue,
192
+ Solid, Qwik or plain HTML — there's no component runtime to adopt. The optional
193
+ `classes` and framework-neutral `behaviors` entrypoints pull in **no** UI
194
+ framework and are SSR-safe. The React, Solid, Qwik, Svelte, and Vue lifecycle
195
+ adapter subpaths remain compatible in 0.7 but are deprecated for removal no
196
+ earlier than 0.8; initialize vanilla behaviors in the framework's ordinary
197
+ mount/cleanup lifecycle instead.
191
198
 
192
199
  Per-framework getting-started guides + runnable example apps live in the repo:
193
200
 
@@ -204,7 +211,8 @@ Per-framework getting-started guides + runnable example apps live in the repo:
204
211
 
205
212
  ## Extras
206
213
 
207
- - **Tokens as data** — `import tokens, { themeColor, cssVars } from '@ponchia/ui/tokens'` (plus `tokens.json`, W3C DTCG `tokens.dtcg.json`, `tokens/resolved.json` for concrete values in canvas/SVG/MapLibre, and `tokens/figma.variables.json` for local Figma Variables import/sync scripts).
214
+ - **Tokens as data** — `import tokens, { themeColor, cssVars } from '@ponchia/ui/tokens'` (plus authored `tokens.json`, a structured DTCG 2025.10 `tokens.dtcg.json`, `tokens/resolved.json` for concrete values in canvas/SVG/MapLibre, and `tokens/figma.variables.json` for local Figma Variables import/sync scripts).
215
+ - **Consumer contract checker** — after an upgrade, run `npx --no-install bronto-ui-check src` to catch unknown literal `ui-*` classes and unresolved Bronto token references in code, style, and template sources before build or deployment. Comments, generated/vendor directories, and Markdown prose are excluded.
208
216
  - **Chart colours for dashboards** — `import charts from '@ponchia/ui/charts.json' with { type: 'json' }` in Node ESM, or the same path through a bundler JSON import (resolved hex per theme; series 1 = your accent) plus the opt-in `@ponchia/ui/css/dataviz.css`.
209
217
  - **Static reports for LLMs** — add `@ponchia/ui/css/report-kit.css` for the complete report vocabulary, or `@ponchia/ui/css/report.css` plus the specific leaves a smaller report needs. Sidecar claim/source contracts can validate against `@ponchia/ui/schemas/report-claims.v1.schema.json`. Full cookbook: `docs/reporting.md`.
210
218
  - **Modern-platform motion** — overlays (modal/drawer/popover), toasts and the `<details>` accordion animate **in and out** with zero JS (`@starting-style` + `allow-discrete`, `::details-content` + `interpolate-size`). Progressive-enhancement extras: `.ui-scroll-progress` / `.ui-scroll-reveal` (scroll-driven, no JS) and `.ui-vt` for View Transitions. All degrade to a static end-state and respect `prefers-reduced-motion`. For smooth **cross-document** navigations, add the document-global one-liner to your own top-level (unlayered) CSS: `@view-transition { navigation: auto; }`.
@@ -1 +1 @@
1
- {"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["dialog.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AACH,sCAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,CAgG3C"}
1
+ {"version":3,"file":"dialog.d.ts","sourceRoot":"","sources":["dialog.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AACH,sCAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,CA8G3C"}
@@ -26,8 +26,21 @@ export function initDialog({ root } = {}) {
26
26
  if (!doc) return noop;
27
27
  const managedDialogs = new Set();
28
28
  const focusRestorers = new Map();
29
+ const warnedNameless = new WeakSet();
29
30
  const canManageDialog = (dlg, origin) => host.contains(origin) || managedDialogs.has(dlg);
30
31
 
32
+ const warnIfNameless = (dlg) => {
33
+ const named =
34
+ dlg.hasAttribute('aria-label') ||
35
+ dlg.hasAttribute('aria-labelledby') ||
36
+ dlg.hasAttribute('title');
37
+ if (named || warnedNameless.has(dlg) || typeof console === 'undefined') return;
38
+ warnedNameless.add(dlg);
39
+ console.warn(
40
+ `[bronto] initDialog(): dialog #${dlg.id || '(without id)'} has no accessible name — add aria-label, aria-labelledby, or title so it is announced as a named dialog.`,
41
+ );
42
+ };
43
+
31
44
  const openFrom = (opener) => {
32
45
  const dlg = byIdInHost(host, opener.getAttribute('data-bronto-open'));
33
46
  if (!dlg || typeof dlg.showModal !== 'function' || dlg.open) return false;
@@ -47,6 +60,7 @@ export function initDialog({ root } = {}) {
47
60
  } catch {
48
61
  return false;
49
62
  }
63
+ warnIfNameless(dlg);
50
64
  managedDialogs.add(dlg);
51
65
  focusRestorers.set(dlg, restoreFocus);
52
66
  dlg.addEventListener('close', restoreFocus, { once: true });
@@ -28,6 +28,10 @@
28
28
  *
29
29
  * SSR-safe, idempotent per modal; returns a cleanup function.
30
30
  *
31
+ * @deprecated Use a native `<dialog>` with `initDialog()`. This controlled
32
+ * non-dialog path remains compatible in 0.7 and is scheduled for removal no
33
+ * earlier than 0.8 because no real consumer adopted it.
34
+ *
31
35
  * @param {import('./internal.js').DelegateOpts} [opts]
32
36
  * @returns {import('./internal.js').Cleanup}
33
37
  */
@@ -1 +1 @@
1
- {"version":3,"file":"modal.d.ts","sourceRoot":"","sources":["modal.js"],"names":[],"mappings":"AAmQA;;;GAGG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qCAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,CA6E3C;;;;;YA3Ga,QAAQ"}
1
+ {"version":3,"file":"modal.d.ts","sourceRoot":"","sources":["modal.js"],"names":[],"mappings":"AAmQA;;;GAGG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,qCAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,CA6E3C;;;;;YA/Ga,QAAQ"}
@@ -288,6 +288,10 @@ function targetIsOwnedPopover(target, modal, state) {
288
288
  *
289
289
  * SSR-safe, idempotent per modal; returns a cleanup function.
290
290
  *
291
+ * @deprecated Use a native `<dialog>` with `initDialog()`. This controlled
292
+ * non-dialog path remains compatible in 0.7 and is scheduled for removal no
293
+ * earlier than 0.8 because no real consumer adopted it.
294
+ *
291
295
  * @param {import('./internal.js').DelegateOpts} [opts]
292
296
  * @returns {import('./internal.js').Cleanup}
293
297
  */
@@ -4,6 +4,8 @@
4
4
  * (`role="separator"`). The behavior keeps `--splitter-pos` and
5
5
  * `aria-valuenow` in sync for keyboard and pointer resizing, then dispatches
6
6
  * `bronto:splitter:resize` with `{ value, orientation }`.
7
+ * Buttons inside the splitter may set `data-bronto-splitter-adjust="-10"` or
8
+ * `"10"` to provide the required single-pointer, non-drag resize path.
7
9
  *
8
10
  * Bronto owns the control affordance only. The host owns pane content,
9
11
  * persistence, min/max policy, collapse behavior, and any saved layout state.
@@ -1 +1 @@
1
- {"version":3,"file":"splitter.d.ts","sourceRoot":"","sources":["splitter.js"],"names":[],"mappings":"AA6OA;;;;;;;;;;;;;GAaG;AACH,wCAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,CAU3C;;;;;WAvPa,MAAM;;;;iBACN,UAAU,GAAG,YAAY"}
1
+ {"version":3,"file":"splitter.d.ts","sourceRoot":"","sources":["splitter.js"],"names":[],"mappings":"AAyPA;;;;;;;;;;;;;;;GAeG;AACH,wCAHW,OAAO,eAAe,EAAE,YAAY,GAClC,OAAO,eAAe,EAAE,OAAO,CAU3C;;;;;WApQa,MAAM;;;;iBACN,UAAU,GAAG,YAAY"}
@@ -1,7 +1,8 @@
1
- import { hasDom, resolveHost, noop, bindOnce, collectHosts } from './internal.js';
1
+ import { hasDom, resolveHost, noop, bindOnce, collectHosts, closestSafe } from './internal.js';
2
2
 
3
3
  const SELECTOR = '[data-bronto-splitter]';
4
4
  const HANDLE_SELECTOR = '.ui-splitter__handle';
5
+ const ADJUST_SELECTOR = '[data-bronto-splitter-adjust]';
5
6
  const DEFAULT_MIN = 20;
6
7
  const DEFAULT_MAX = 80;
7
8
  const DEFAULT_VALUE = 50;
@@ -218,11 +219,22 @@ function wireSplitter(splitter) {
218
219
  splitter.ownerDocument.addEventListener('pointercancel', onPointerUp);
219
220
  };
220
221
 
222
+ const onClick = (event) => {
223
+ const control = closestSafe(event.target, ADJUST_SELECTOR);
224
+ if (!control || control.closest(SELECTOR) !== splitter) return;
225
+ const delta = num(control.getAttribute('data-bronto-splitter-adjust'), Number.NaN);
226
+ if (!Number.isFinite(delta) || delta === 0) return;
227
+ event.preventDefault();
228
+ apply(value + delta);
229
+ };
230
+
221
231
  handle.addEventListener('keydown', onKeydown);
222
232
  handle.addEventListener('pointerdown', onPointerDown);
233
+ splitter.addEventListener('click', onClick);
223
234
  return () => {
224
235
  handle.removeEventListener('keydown', onKeydown);
225
236
  handle.removeEventListener('pointerdown', onPointerDown);
237
+ splitter.removeEventListener('click', onClick);
226
238
  splitter.ownerDocument.removeEventListener('pointermove', onPointerMove);
227
239
  splitter.ownerDocument.removeEventListener('pointerup', onPointerUp);
228
240
  splitter.ownerDocument.removeEventListener('pointercancel', onPointerUp);
@@ -241,6 +253,8 @@ function wireSplitter(splitter) {
241
253
  * (`role="separator"`). The behavior keeps `--splitter-pos` and
242
254
  * `aria-valuenow` in sync for keyboard and pointer resizing, then dispatches
243
255
  * `bronto:splitter:resize` with `{ value, orientation }`.
256
+ * Buttons inside the splitter may set `data-bronto-splitter-adjust="-10"` or
257
+ * `"10"` to provide the required single-pointer, non-drag resize path.
244
258
  *
245
259
  * Bronto owns the control affordance only. The host owns pane content,
246
260
  * persistence, min/max policy, collapse behavior, and any saved layout state.
@@ -22,8 +22,9 @@ export function applyStoredTheme({ storageKey, root }?: ApplyThemeOpts): void;
22
22
  * persists to localStorage, and **always** sets `data-theme` on <html>
23
23
  * (a theme is document-global). State is reflected via `aria-pressed`
24
24
  * and a `bronto:themechange` CustomEvent ({ detail: { theme } }) is
25
- * dispatched on <html> so consumers can sync their own UI without
26
- * racing the click handler. A control may set
25
+ * dispatched on <html> for both explicit changes and OS preference changes,
26
+ * so consumers can sync their own UI without racing the click/media handler.
27
+ * A control may set
27
28
  * `data-bronto-theme-toggle="dark"` to force a specific theme.
28
29
  *
29
30
  * `root` scopes event delegation and which controls are queried/reflected
@@ -1 +1 @@
1
- {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["theme.js"],"names":[],"mappings":"AAuBA;;;;;;;;;GASG;AAEH;;;;;;;GAOG;AACH,wDAHW,cAAc,GACZ,IAAI,CAahB;AAED;;;;;;;;;;;;;;GAcG;AACH,uDAHW,gBAAgB,GAAG,OAAO,eAAe,EAAE,YAAY,GACrD,OAAO,eAAe,EAAE,OAAO,CAgH3C;;;;;;;;;;6BAzJY,gBAAgB,GAAG;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE;;;;;WAIpC,OAAO,GAAG,MAAM"}
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["theme.js"],"names":[],"mappings":"AAuBA;;;;;;;;;GASG;AAEH;;;;;;;GAOG;AACH,wDAHW,cAAc,GACZ,IAAI,CAahB;AAED;;;;;;;;;;;;;;;GAeG;AACH,uDAHW,gBAAgB,GAAG,OAAO,eAAe,EAAE,YAAY,GACrD,OAAO,eAAe,EAAE,OAAO,CAmH3C;;;;;;;;;;6BA7JY,gBAAgB,GAAG;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE;;;;;WAIpC,OAAO,GAAG,MAAM"}
@@ -58,8 +58,9 @@ export function applyStoredTheme({ storageKey = 'bronto-theme', root } = {}) {
58
58
  * persists to localStorage, and **always** sets `data-theme` on <html>
59
59
  * (a theme is document-global). State is reflected via `aria-pressed`
60
60
  * and a `bronto:themechange` CustomEvent ({ detail: { theme } }) is
61
- * dispatched on <html> so consumers can sync their own UI without
62
- * racing the click handler. A control may set
61
+ * dispatched on <html> for both explicit changes and OS preference changes,
62
+ * so consumers can sync their own UI without racing the click/media handler.
63
+ * A control may set
63
64
  * `data-bronto-theme-toggle="dark"` to force a specific theme.
64
65
  *
65
66
  * `root` scopes event delegation and which controls are queried/reflected
@@ -98,6 +99,11 @@ export function initThemeToggle({ storageKey = 'bronto-theme', root } = {}) {
98
99
  return prefersDark() ? 'dark' : 'light';
99
100
  };
100
101
 
102
+ const emitThemeChange = (theme) => {
103
+ const ThemeEvent = doc.defaultView?.CustomEvent ?? CustomEvent;
104
+ docEl.dispatchEvent(new ThemeEvent('bronto:themechange', { detail: { theme }, bubbles: true }));
105
+ };
106
+
101
107
  const reflect = () => {
102
108
  const c = current();
103
109
  collectHosts(host, '[data-bronto-theme-toggle]').forEach((el) => {
@@ -134,7 +140,7 @@ export function initThemeToggle({ storageKey = 'bronto-theme', root } = {}) {
134
140
  clearSchemeListener();
135
141
  return;
136
142
  }
137
- reflect();
143
+ emitThemeChange(current());
138
144
  }
139
145
 
140
146
  const onThemeChange = () => {
@@ -156,9 +162,7 @@ export function initThemeToggle({ storageKey = 'bronto-theme', root } = {}) {
156
162
  /* storage blocked — theme still applies for this session */
157
163
  }
158
164
  reflect();
159
- docEl.dispatchEvent(
160
- new CustomEvent('bronto:themechange', { detail: { theme: next }, bubbles: true }),
161
- );
165
+ emitThemeChange(next);
162
166
  };
163
167
 
164
168
  return bindOnce(host, 'themeToggle', () => {