@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.
- package/CHANGELOG.md +210 -0
- package/MIGRATIONS.json +14 -0
- package/README.md +14 -6
- package/behaviors/dialog.d.ts.map +1 -1
- package/behaviors/dialog.js +14 -0
- package/behaviors/modal.d.ts +4 -0
- package/behaviors/modal.d.ts.map +1 -1
- package/behaviors/modal.js +4 -0
- package/behaviors/splitter.d.ts +2 -0
- package/behaviors/splitter.d.ts.map +1 -1
- package/behaviors/splitter.js +15 -1
- package/behaviors/theme.d.ts +3 -2
- package/behaviors/theme.d.ts.map +1 -1
- package/behaviors/theme.js +10 -6
- package/bin/bronto-ui-check.mjs +286 -0
- package/classes/classes.json +86 -7
- package/classes/index.d.ts +53 -1
- package/classes/index.js +60 -0
- package/classes/vscode.css-custom-data.json +24 -0
- package/css/app.css +14 -3
- package/css/disclosure.css +15 -5
- package/css/dots.css +43 -18
- package/css/feedback.css +46 -8
- package/css/forms.css +3 -3
- package/css/navigation.css +1 -1
- package/css/overlay.css +19 -5
- package/css/primitives.css +101 -6
- package/css/report.css +0 -40
- package/css/site.css +19 -3
- package/css/skins.css +97 -3
- package/css/state.css +161 -0
- package/css/table.css +1 -1
- package/css/tokens.css +6 -0
- package/css/workbench.css +151 -0
- package/dist/bronto.css +1 -1
- package/dist/css/app.css +1 -1
- package/dist/css/disclosure.css +1 -1
- package/dist/css/dots.css +1 -1
- package/dist/css/feedback.css +1 -1
- package/dist/css/forms.css +1 -1
- package/dist/css/navigation.css +1 -1
- package/dist/css/overlay.css +1 -1
- package/dist/css/primitives.css +1 -1
- package/dist/css/report-kit.css +1 -1
- package/dist/css/report.css +1 -1
- package/dist/css/site.css +1 -1
- package/dist/css/skins.css +1 -1
- package/dist/css/state.css +1 -1
- package/dist/css/table.css +1 -1
- package/dist/css/tokens.css +1 -1
- package/dist/css/workbench.css +1 -1
- package/docs/adr/0001-color-system.md +32 -3
- package/docs/adr/0004-prune-unused-adapters.md +34 -0
- package/docs/architecture.md +14 -10
- package/docs/command.md +18 -4
- package/docs/contrast.md +102 -18
- package/docs/migrations/0.6-to-0.7.md +85 -0
- package/docs/package-contract.md +10 -5
- package/docs/reference.md +52 -1
- package/docs/reporting.md +8 -8
- package/docs/stability.md +39 -9
- package/docs/state.md +51 -1
- package/docs/theming.md +67 -5
- package/docs/usage.md +119 -19
- package/docs/workbench.md +99 -6
- package/llms.txt +7 -3
- package/package.json +13 -3
- package/qwik/index.d.ts.map +1 -1
- package/qwik/index.js +4 -0
- package/react/index.d.ts.map +1 -1
- package/react/index.js +4 -0
- package/solid/index.d.ts.map +1 -1
- package/solid/index.js +4 -0
- package/svelte/index.d.ts.map +1 -1
- package/svelte/index.js +4 -0
- package/tokens/figma.variables.json +84 -0
- package/tokens/index.d.ts +2 -2
- package/tokens/index.js +23 -0
- package/tokens/index.json +12 -0
- package/tokens/resolved.json +6 -0
- package/tokens/skins.js +117 -7
- package/tokens/tokens.dtcg.json +2514 -399
- package/vue/index.d.ts.map +1 -1
- 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
|
[](https://www.npmjs.com/package/@ponchia/ui)
|
|
4
4
|
[](https://www.npmjs.com/package/@ponchia/ui#provenance)
|
|
5
5
|
[](https://github.com/Ponchia/bronto-ui/blob/main/package.json)
|
|
6
|
-
[](https://github.com/Ponchia/bronto-ui/blob/main/scripts/check-dist.mjs)
|
|
7
7
|
[](https://github.com/Ponchia/bronto-ui/actions/workflows/ci.yml)
|
|
8
8
|
[](https://scorecard.dev/viewer/?uri=github.com/Ponchia/bronto-ui)
|
|
9
9
|
[](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.
|
|
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
|
+
**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,
|
|
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`,
|
|
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,
|
|
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"}
|
package/behaviors/dialog.js
CHANGED
|
@@ -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 });
|
package/behaviors/modal.d.ts
CHANGED
|
@@ -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
|
*/
|
package/behaviors/modal.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modal.d.ts","sourceRoot":"","sources":["modal.js"],"names":[],"mappings":"AAmQA;;;GAGG;AAEH
|
|
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"}
|
package/behaviors/modal.js
CHANGED
|
@@ -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
|
*/
|
package/behaviors/splitter.d.ts
CHANGED
|
@@ -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":"
|
|
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"}
|
package/behaviors/splitter.js
CHANGED
|
@@ -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.
|
package/behaviors/theme.d.ts
CHANGED
|
@@ -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>
|
|
26
|
-
* racing the click handler.
|
|
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
|
package/behaviors/theme.d.ts.map
CHANGED
|
@@ -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
|
|
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"}
|
package/behaviors/theme.js
CHANGED
|
@@ -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>
|
|
62
|
-
* racing the click handler.
|
|
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
|
-
|
|
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
|
-
|
|
160
|
-
new CustomEvent('bronto:themechange', { detail: { theme: next }, bubbles: true }),
|
|
161
|
-
);
|
|
165
|
+
emitThemeChange(next);
|
|
162
166
|
};
|
|
163
167
|
|
|
164
168
|
return bindOnce(host, 'themeToggle', () => {
|