@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.
- package/CHANGELOG.md +178 -0
- package/README.md +3 -3
- package/classes/classes.json +79 -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 +7 -7
- package/css/feedback.css +11 -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/site.css +11 -5
- 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/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/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/contrast.md +102 -18
- package/docs/migrations/0.7-to-0.8.md +216 -0
- package/docs/package-contract.md +1 -0
- package/docs/reference.md +52 -1
- package/docs/reporting.md +8 -8
- package/docs/stability.md +31 -2
- package/docs/state.md +51 -1
- package/docs/theming.md +18 -0
- package/docs/usage.md +62 -2
- package/docs/workbench.md +83 -4
- package/llms.txt +1 -1
- package/package.json +13 -12
- 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 +7 -1
package/docs/stability.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Public API stability
|
|
2
2
|
|
|
3
3
|
`@ponchia/ui` is pre-1.0. Breaking changes ship in the minor (`0.x.0`), and
|
|
4
|
-
patches are non-breaking. In practical terms: **PATCH releases (`0.
|
|
4
|
+
patches are non-breaking. In practical terms: **PATCH releases (`0.8.x`) are
|
|
5
5
|
non-breaking bug-fixes and additive changes — safe to upgrade without review;
|
|
6
6
|
MINOR releases (`0.x.0`) may include breaking changes and consumers should
|
|
7
7
|
review the CHANGELOG before upgrading.** Pin `~0.x` (tilde) to accept only
|
|
@@ -68,13 +68,41 @@ Package-proven is necessary but does not establish demand. During the catalog
|
|
|
68
68
|
freeze, do not expand package-only surfaces. Recheck this table against real
|
|
69
69
|
consumer upgrades before the 1.0 release candidate.
|
|
70
70
|
|
|
71
|
+
> **The 0.8.0 consumer audit — read this before trusting the table below.** A
|
|
72
|
+
> full-surface audit of the largest downstream consumer (a Yjs-collaborative
|
|
73
|
+
> spatial canvas workspace: React, Vite, ~10k lines of its own CSS) found it
|
|
74
|
+
> using **26 of the 646 published classes**, and hand-rebuilding much of the
|
|
75
|
+
> rest — 10 bespoke empty states, 15 error surfaces, 17 control bars, three
|
|
76
|
+
> parallel severity vocabularies, its own Markdown prose layer, its own diff
|
|
77
|
+
> rows, its own meters, and two copies of visually-hidden text. Thirteen
|
|
78
|
+
> primitives that map almost exactly onto what it built return **zero** uses:
|
|
79
|
+
> `ui-state`, `ui-prose`, `ui-diff`, `ui-code`, `ui-meter`, `ui-progress`,
|
|
80
|
+
> `ui-toolstrip`, `ui-dot`, `ui-chip`, `ui-tag`, `ui-timeline`, `ui-steps`,
|
|
81
|
+
> `ui-job`.
|
|
82
|
+
>
|
|
83
|
+
> The lesson for this document is not about that consumer. It is that **"no
|
|
84
|
+
> inspected consumer imports the surface" has been measuring discoverability,
|
|
85
|
+
> not demand.** Those surfaces were not rejected; they were never found. The
|
|
86
|
+
> opt-in leaf model is right, but nothing tells a consumer which leaf it is
|
|
87
|
+
> about to reimplement — and `bronto-ui-check`, which catches part of it, was
|
|
88
|
+
> installed in that consumer and had never been run. Before any 1.0 decision
|
|
89
|
+
> retires a package-proven surface for lack of adoption, confirm the surface was
|
|
90
|
+
> *reachable*: named in the consumer's imports, or at least in a leaf it
|
|
91
|
+
> imports. Non-adoption of an unimported leaf is not evidence.
|
|
92
|
+
>
|
|
93
|
+
> The same audit produced this release's four consumer-driven changes (the tap
|
|
94
|
+
> target floor, the safe-area tokens, `ui-button__label`, and the dense tier)
|
|
95
|
+
> and the ADR-0001 canvas amendment. That is the intended loop: the consumer's
|
|
96
|
+
> local classes are the backlog, resolving either to a deletion there or an
|
|
97
|
+
> addition here.
|
|
98
|
+
|
|
71
99
|
| Surface family | Current evidence | 1.0 disposition |
|
|
72
100
|
| --- | --- | --- |
|
|
73
101
|
| Core CSS, class recipes, vanilla behaviors, tokens, and Tailwind bridge | Downstream-proven across five inspected non-example app, site, and service consumers. | Stabilize names and behavior contracts. Use consumer upgrades as the release-candidate proof. |
|
|
74
102
|
| Report, provenance, analytical CSS, annotations, glyphs, skins, workbench CSS, chart data, and Vega theme | Downstream-proven across four inspected report, site, dashboard, and tool consumers. | Keep opt-in. Stabilize the consumed paths; do not broaden the catalog during the freeze. |
|
|
75
103
|
| Controlled non-`<dialog>` modal | Package-proven by stack, portal, late-node, focus, and cleanup regressions. None of ten inspected non-example consumers initializes `initModal`. | Deprecated in 0.7; remove no earlier than 0.8 unless a real consumer adopts it. Native `<dialog>` + `initDialog` is the stable path. |
|
|
76
104
|
| React, Solid, Qwik, Svelte, and Vue lifecycle adapters | Package-proven by packed examples, types, and lifecycle tests. None of ten inspected non-example consumers imports an adapter entrypoint. | Deprecated in 0.7; remove no earlier than 0.8 unless a real consumer adopts one. Vanilla behaviors remain stable. |
|
|
77
|
-
| Mermaid, D2, Shiki, Figma Variables, and the report-claims schema | Package-proven by generated-data, render, schema, and drift checks. No inspected non-example consumer currently supplies downstream proof for every path. | Keep compatible through 0.
|
|
105
|
+
| Mermaid, D2, Shiki, Figma Variables, and the report-claims schema | Package-proven by generated-data, render, schema, and drift checks. No inspected non-example consumer currently supplies downstream proof for every path. | Keep compatible through 0.8.x, but decide each 1.0 contract from adoption evidence rather than generator coverage alone. |
|
|
78
106
|
|
|
79
107
|
After 1.0, breaking changes move to majors. Until then, the table below is the
|
|
80
108
|
current public-surface matrix and the release policy above still applies.
|
|
@@ -85,6 +113,7 @@ current public-surface matrix and the release policy above still applies.
|
|
|
85
113
|
| JS module format | Stable | JS subpaths are ESM-only. CommonJS consumers use dynamic `import()`. |
|
|
86
114
|
| CSS class names (`.ui-*`) | Stable | Names and documented modifier semantics are public. Internal selector structure and leaf-file boundaries may change. |
|
|
87
115
|
| Class recipes (`@ponchia/ui/classes`) | Stable | Exported `cls`, `ui`, `cx`, `attrs`, recipe names, ARIA attribute helper names, and option unions are public. |
|
|
116
|
+
| Severity ladder (`SEVERITY_LEVELS`, `severity()`, `.ui-severity*`) | Stable additive | The tier names and **their order** are public: `critical` › `error` › `warning` › `notice` › `ok`, with `unknown` deliberately outside the ordering as the fallback for "not measured". `severity()` returns `{ class, 'data-level' }`, and `data-level` is the public attribute contract — a host may carry it on its own element and read `var(--severity-tone)`. New tiers would be a breaking reorder, not an addition, so the ladder is closed; new *parts* (`chip`/`dot`/`row`) are additive. Ships in the opt-in `css/state.css`, not the default bundle. |
|
|
88
117
|
| Class vocabulary as data (`@ponchia/ui/classes.json`, `@ponchia/ui/vscode.css-custom-data.json`) | Stable additive | The JSON shape (`groups`/`classes`/`states`/`customProperties`) and class/custom-property entries are public — for validating markup from a non-JS/non-TS host or editor integration. Generated from `cls` and CSS selectors; `classes.json`, `.d.ts`, reference docs, and VS Code custom data are drift-checked together. New classes/hooks are additive. |
|
|
89
118
|
| Design tokens | Stable names/roles | Token names and documented roles are public. Exact values and generated colour math outputs may change for visual tuning before 1.0. |
|
|
90
119
|
| `--accent-1..6` | Stable names/roles | A subtle-to-bold accent ramp derived from `--accent`. Exact resolved values are visual tuning; algorithm changes require release-note visibility and resolver/browser checks. |
|
package/docs/state.md
CHANGED
|
@@ -124,14 +124,64 @@ For indeterminate jobs, omit the progress block or omit `aria-valuenow`, and
|
|
|
124
124
|
make the written state clear ("Running", "Waiting for worker", "Retrying").
|
|
125
125
|
Use `ui-job--compact` for dense queues.
|
|
126
126
|
|
|
127
|
+
## Severity — `.ui-severity`
|
|
128
|
+
|
|
129
|
+
`.ui-state` answers *what is this thing doing*. Severity answers *how bad is
|
|
130
|
+
it*. Bronto already shipped the tones as per-component modifiers, but never the
|
|
131
|
+
**scale** — the tier names, their order, and the attribute carrying them. So
|
|
132
|
+
every consumer invents the ladder, and inside one app it drifts: one surface
|
|
133
|
+
saying `critical|error|warning|note`, the next `bad|warn`, a third
|
|
134
|
+
`critical|warning|info|ok`, under two different attribute names. Findings then
|
|
135
|
+
do not sort against alerts, and a filter written for one list misses the other.
|
|
136
|
+
|
|
137
|
+
The ladder, worst to best:
|
|
138
|
+
|
|
139
|
+
| Level | Means |
|
|
140
|
+
| --- | --- |
|
|
141
|
+
| `critical` | Broken now, and still losing something. |
|
|
142
|
+
| `error` | Something failed; it is not currently getting worse. |
|
|
143
|
+
| `warning` | A threshold was crossed; nothing has failed yet. |
|
|
144
|
+
| `notice` | Worth reading, no action implied. |
|
|
145
|
+
| `ok` | Checked and healthy — an **assertion**, not the absence of news. |
|
|
146
|
+
| `unknown` | Not measured, stale, or the check itself failed. |
|
|
147
|
+
|
|
148
|
+
`unknown` sits **outside** the ordering on purpose. It is not "slightly worse
|
|
149
|
+
than ok", it is "we do not know" — and collapsing it into `ok` is how a dead
|
|
150
|
+
collector reads as a healthy system. `SEVERITY_LEVELS` therefore excludes it.
|
|
151
|
+
|
|
152
|
+
The level travels on `data-level`, one attribute name, so the same selector
|
|
153
|
+
works on a chip, a row, a dot, or your own element via `var(--severity-tone)`:
|
|
154
|
+
|
|
155
|
+
```html
|
|
156
|
+
<span class="ui-severity" data-level="critical">Critical</span>
|
|
157
|
+
|
|
158
|
+
<li class="ui-severity-row" data-level="warning">
|
|
159
|
+
<span class="ui-severity-row__title">Disk 84% on kpi-1</span>
|
|
160
|
+
<span class="ui-severity-row__meta">12m</span>
|
|
161
|
+
</li>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Colour is never the only channel (WCAG 1.4.1): `.ui-severity` carries an
|
|
165
|
+
author-written label, and `.ui-severity-dot` is only for rows that **also** name
|
|
166
|
+
their level in text.
|
|
167
|
+
|
|
127
168
|
## Recipe
|
|
128
169
|
|
|
129
170
|
```js
|
|
130
|
-
import { ui } from '@ponchia/ui/classes';
|
|
171
|
+
import { ui, severity, SEVERITY_LEVELS } from '@ponchia/ui/classes';
|
|
131
172
|
|
|
132
173
|
ui.state({ state: 'saving', busy: true }); // "ui-state ui-state--saving ui-state--busy"
|
|
133
174
|
ui.state({ state: 'conflict' }); // "ui-state ui-state--conflict"
|
|
134
175
|
ui.job({ state: 'running' }); // "ui-job ui-job--running"
|
|
176
|
+
|
|
177
|
+
// Bundles the class WITH data-level, so the attribute that carries the meaning
|
|
178
|
+
// cannot be forgotten — the class alone paints the neutral tone and silently
|
|
179
|
+
// loses the level.
|
|
180
|
+
severity('critical'); // { class: 'ui-severity', 'data-level': 'critical' }
|
|
181
|
+
severity('warning', { part: 'row' }); // { class: 'ui-severity-row', … }
|
|
182
|
+
severity('nope'); // { class: 'ui-severity', 'data-level': 'unknown' }
|
|
183
|
+
|
|
184
|
+
SEVERITY_LEVELS; // ['critical','error','warning','notice','ok'] — sort/filter from this
|
|
135
185
|
```
|
|
136
186
|
|
|
137
187
|
## Scope
|
package/docs/theming.md
CHANGED
|
@@ -140,6 +140,24 @@ you change CSS `--accent` later.
|
|
|
140
140
|
[ADR-0003](adr/0003-theme-model.md) for the theme-model rationale.
|
|
141
141
|
- **Radius** — `--radius-sm … --radius-xl`, `--radius-pill`. The Nothing
|
|
142
142
|
default is near-sharp; raise these for a softer brand.
|
|
143
|
+
- **Tap targets** — `--tap-target` (44px, WCAG 2.5.5 / iOS HIG / Material) is
|
|
144
|
+
the floor every control floats to inside `@media (pointer: coarse)`;
|
|
145
|
+
`--tap-target-min` (24px) is the WCAG 2.5.8 AA minimum used by controls that
|
|
146
|
+
only have to clear the smaller bar. Both are authored as `max(px, rem)` on
|
|
147
|
+
purpose: they scale up with a larger root font but **cannot shrink below the
|
|
148
|
+
standard** if you re-point `html { font-size }`. If you override them, keep
|
|
149
|
+
the clamp — a bare rem is how a 44px floor quietly becomes 43.5px. Raise them
|
|
150
|
+
for a glove-friendly or kiosk build; do not lower them.
|
|
151
|
+
- **Safe areas** — `--safe-area-top / -right / -bottom / -left` default to
|
|
152
|
+
`env(safe-area-inset-*, 0px)`, so they are 0 everywhere except a device with
|
|
153
|
+
a display cutout or a gesture bar. Every viewport-anchored surface Bronto
|
|
154
|
+
ships reads them: the app rail and topbar, a sticky site header, the skip
|
|
155
|
+
link, both toast stacks, the drawer modal and the lightbox. Override them
|
|
156
|
+
when your host supplies its own insets — an embedded webview, a kiosk frame,
|
|
157
|
+
or a test runner that cannot emulate `env()`, which is the reason the values
|
|
158
|
+
are indirected through custom properties rather than called at the point of
|
|
159
|
+
use. Consumers positioning their own floating chrome should follow the same
|
|
160
|
+
convention: `inset-block-end: max(<your offset>, var(--safe-area-bottom))`.
|
|
143
161
|
- **Type** — `--display` (dot-matrix face), `--mono`, `--sans`. Override
|
|
144
162
|
to drop Doto or swap the body face; the token layer keeps working even
|
|
145
163
|
if you self-host fonts (see the `fonts.css` note in the README).
|
package/docs/usage.md
CHANGED
|
@@ -167,15 +167,50 @@ a `.ui-legend` key, and fallback data. Full LLM/static report cookbook:
|
|
|
167
167
|
|
|
168
168
|
## Buttons: variant and size
|
|
169
169
|
|
|
170
|
-
- **primary
|
|
170
|
+
- **primary is the bare `ui-button`.** There is no `--primary` and no
|
|
171
|
+
`--accent`: the unmodified class already paints the accent fill, and the
|
|
172
|
+
variants below all step *down* from it. Aim for one per view. Consumers reach
|
|
173
|
+
for a `--primary` or `--accent` modifier often enough that it is worth
|
|
174
|
+
stating plainly: neither exists. Writing one is harmless *and invisible* —
|
|
175
|
+
the unknown class does nothing and the button still looks right, which is why
|
|
176
|
+
the mistake survives review. `bronto-ui-check` is what catches it.
|
|
171
177
|
- **ghost** — secondary actions; the default for "another button here".
|
|
172
178
|
- **subtle** — tertiary / low-stakes (toolbar, inline).
|
|
179
|
+
- **danger** — destructive confirmation.
|
|
173
180
|
- Size: default everywhere; `--sm` for dense tooling (toolbars,
|
|
174
|
-
pagination, table row actions), `--lg` for a hero CTA only
|
|
181
|
+
pagination, table row actions), `--lg` for a hero CTA only, `--dense` when a
|
|
182
|
+
bar's *height* is the constraint (a pane title bar). `--dense` lowers only the
|
|
183
|
+
visual floor — coarse pointers still get the full `--tap-target`, so a control
|
|
184
|
+
you shrink for a mouse is never shrunk for a finger.
|
|
175
185
|
- Loading is **not** a class: set `aria-busy="true"` (+ `disabled`); the
|
|
176
186
|
spinner is CSS. This is the ARIA-driven contract — see reference.md
|
|
177
187
|
→ "Composition & state".
|
|
178
188
|
|
|
189
|
+
## Empty state vs invite
|
|
190
|
+
|
|
191
|
+
Both use `ui-empty-state`, and the slots are the same three parts — a quiet
|
|
192
|
+
`__glyph`, one sentence of full ink in `__lead`, a quieter `__hint`. What
|
|
193
|
+
differs is the job:
|
|
194
|
+
|
|
195
|
+
- **Plain `ui-empty-state`** *reports absence*: a dashed card saying this region
|
|
196
|
+
has no rows today. Use it for a list, a table, a results pane.
|
|
197
|
+
- **`--invite`** *offers the next action*: no dashed box (there is nothing to
|
|
198
|
+
outline — the surface itself is what you are being invited into) and it
|
|
199
|
+
centres in whatever block space it is given. Use it for a surface the user is
|
|
200
|
+
meant to fill.
|
|
201
|
+
|
|
202
|
+
```html
|
|
203
|
+
<div class="ui-empty-state ui-empty-state--invite">
|
|
204
|
+
<span class="ui-empty-state__glyph" aria-hidden="true">+</span>
|
|
205
|
+
<p class="ui-empty-state__lead">Nothing pinned yet</p>
|
|
206
|
+
<p class="ui-empty-state__hint">Drop a file here, or press ⌘K</p>
|
|
207
|
+
</div>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Without the slots, every empty surface in an app re-invents these three parts
|
|
211
|
+
under a different name and they drift — one has a glyph, the next has two
|
|
212
|
+
sentences at the same weight, a third is a bare `<p>`.
|
|
213
|
+
|
|
179
214
|
## Link vs link--cta
|
|
180
215
|
|
|
181
216
|
Plain `ui-link` for in-flow links. `ui-link--cta` is the eyebrow-faced
|
|
@@ -278,6 +313,31 @@ without it these widgets are unlabelled or unannounced:
|
|
|
278
313
|
- **Icon-only buttons** (`ui-button--icon` and any glyph-only control) carry no
|
|
279
314
|
text node, so they're nameless to AT — give them an `aria-label`
|
|
280
315
|
(`<button class="ui-button ui-button--icon" aria-label="Delete">`).
|
|
316
|
+
Prefer `ui-button__label` when the same control also appears with its word
|
|
317
|
+
visible, or when a test reads the button by its text. Wrap the text in the
|
|
318
|
+
slot and let `--icon` decide whether it is painted — the markup does not
|
|
319
|
+
change, the accessible name survives, and no `aria-label` can drift out of
|
|
320
|
+
sync with the visible wording:
|
|
321
|
+
|
|
322
|
+
```js
|
|
323
|
+
// The mask comes from renderGlyph(..., { render: 'mask' }) — there is no
|
|
324
|
+
// --glyph-* token.
|
|
325
|
+
const mask = renderGlyph('trash', { render: 'mask' });
|
|
326
|
+
el.innerHTML =
|
|
327
|
+
`<button class="ui-button ui-button--icon">` +
|
|
328
|
+
`<span class="ui-icon" style="--icon-mask: ${mask}"></span>` +
|
|
329
|
+
`<span class="ui-button__label">Delete</span>` +
|
|
330
|
+
`</button>`;
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
Drop `--icon` and the same markup renders glyph + word. The slot also
|
|
334
|
+
ellipsises rather than wrapping, so a labelled button in a width-constrained
|
|
335
|
+
bar shrinks instead of pushing its neighbours out.
|
|
336
|
+
- **`ui-button--dense`** is for bars whose height is the constraint — a pane
|
|
337
|
+
title bar, a packed toolbar, a table row's actions. It lowers only the
|
|
338
|
+
*visual* floor, to the WCAG 2.5.8 24px minimum. The coarse-pointer block
|
|
339
|
+
still floats it to the full `--tap-target`, so a control you shrink for a
|
|
340
|
+
mouse is never shrunk for a finger.
|
|
281
341
|
- **`ui-sitenav` / `ui-app-nav`** — signal the current link with
|
|
282
342
|
`aria-current="page"` (both honour it; `ui-app-nav` also accepts the
|
|
283
343
|
visual-only `.is-active`, but prefer `aria-current`).
|
package/docs/workbench.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Workbench — split panes, toolstrips, inspector, properties, selection bar
|
|
1
|
+
# Workbench — panes, split panes, toolstrips, inspector, properties, selection bar
|
|
2
2
|
|
|
3
3
|
`@ponchia/ui/css/workbench.css` is an opt-in set of primitives for **real
|
|
4
4
|
tools**: resizable split panes, compact toolstrips, button-mode segmented
|
|
@@ -52,6 +52,29 @@ hiding, and placement.
|
|
|
52
52
|
</header>
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
+
### Pane-scale — `.ui-toolstrip--pane`
|
|
56
|
+
|
|
57
|
+
The app has one toolstrip; a workbench full of panes has one *per pane*, and
|
|
58
|
+
those are a different thing. The difference is framing, not density: a pane bar
|
|
59
|
+
is a row inside a surface that already has a border, so it drops its own frame
|
|
60
|
+
and corners and rules off from the content below.
|
|
61
|
+
|
|
62
|
+
It also refuses to wrap. A pane bar usually sits directly above content that may
|
|
63
|
+
be a live terminal, an editor, or a video — and a second row would resize that
|
|
64
|
+
content every time a control appears. The row scrolls instead, and
|
|
65
|
+
`.ui-toolstrip__fill` marks the one element that absorbs slack and gives it back
|
|
66
|
+
first (a title, a path, a filter input), so nothing else is pushed out of reach.
|
|
67
|
+
|
|
68
|
+
```html
|
|
69
|
+
<div class="ui-toolstrip ui-toolstrip--pane">
|
|
70
|
+
<button class="ui-button ui-button--subtle ui-button--dense" type="button">Run</button>
|
|
71
|
+
<span class="ui-toolstrip__fill ui-mono">src/server/routes/reports.ts</span>
|
|
72
|
+
<button class="ui-button ui-button--ghost ui-button--icon ui-button--dense" type="button">
|
|
73
|
+
<span class="ui-button__label">Close</span>
|
|
74
|
+
</button>
|
|
75
|
+
</div>
|
|
76
|
+
```
|
|
77
|
+
|
|
55
78
|
## Button segmented control — `.ui-segmented-buttons`
|
|
56
79
|
|
|
57
80
|
Use `.ui-segmented-buttons` when each option is a real command button and the
|
|
@@ -170,11 +193,67 @@ A raised bar of actions on the current selection: a `__count` on one side,
|
|
|
170
193
|
</div>
|
|
171
194
|
```
|
|
172
195
|
|
|
196
|
+
### Anchoring a floating bar
|
|
197
|
+
|
|
198
|
+
Both `--floating` bars are raised but position-less, so every consumer
|
|
199
|
+
re-derives the same thing — including the `max(offset, inset)` shape, which is
|
|
200
|
+
the part people get wrong by writing a bare offset that a phone then swallows
|
|
201
|
+
under the home indicator. `--anchored` centres the bar against its positioned
|
|
202
|
+
ancestor and clears the safe area:
|
|
203
|
+
|
|
204
|
+
```html
|
|
205
|
+
<div class="ui-selectionbar ui-selectionbar--anchored">…</div>
|
|
206
|
+
|
|
207
|
+
<!-- The bar that must NOT sit under the thumb: recovery, destructive actions. -->
|
|
208
|
+
<div class="ui-selectionbar ui-selectionbar--anchor-block-start">…</div>
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
`--anchored` alone means bottom. Override the gap with `--anchor-offset`. The
|
|
212
|
+
host still owns `z-index`, because only it knows what else is on the canvas.
|
|
213
|
+
|
|
214
|
+
## Pane — `.ui-pane`
|
|
215
|
+
|
|
216
|
+
`.ui-panel` is a padded card and `.ui-inspector` is head-plus-body; neither is a
|
|
217
|
+
*window*. A pane is what a canvas node, a floating tool window, or a dockable
|
|
218
|
+
panel needs: a header you can drag, a title that renames in place, an actions
|
|
219
|
+
slot that survives a narrow pane, and a body that owns the rest.
|
|
220
|
+
|
|
221
|
+
```html
|
|
222
|
+
<article class="ui-pane">
|
|
223
|
+
<header class="ui-pane__head">
|
|
224
|
+
<strong class="ui-pane__title">deploy.log</strong>
|
|
225
|
+
<span class="ui-pane__actions">
|
|
226
|
+
<button class="ui-button ui-button--subtle ui-button--icon ui-button--dense" type="button">
|
|
227
|
+
<span class="ui-button__label">Focus</span>
|
|
228
|
+
</button>
|
|
229
|
+
</span>
|
|
230
|
+
</header>
|
|
231
|
+
<div class="ui-pane__body">…</div>
|
|
232
|
+
</article>
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Two behaviours are worth knowing, because both come from a real failure:
|
|
236
|
+
|
|
237
|
+
- **The title gives up space first.** It is the only thing in the header that
|
|
238
|
+
can be truncated without losing a function.
|
|
239
|
+
- **`__actions` scrolls rather than pushing.** Sized `flex: 0 1 auto` with
|
|
240
|
+
`overflow-x: auto`, so a pane narrow enough to run out of room scrolls its
|
|
241
|
+
controls instead of pushing the last one past the clipped edge — which is how
|
|
242
|
+
an app ends up with a Focus or Disconnect button that exists, is in the a11y
|
|
243
|
+
tree, and cannot be reached.
|
|
244
|
+
|
|
245
|
+
Swap the title for `.ui-pane__title-input` to rename in place. It inherits the
|
|
246
|
+
type it replaces, so the swap moves no layout; only the accent border says you
|
|
247
|
+
are typing a name now. The host owns dragging, z-order, focus policy and
|
|
248
|
+
persistence — this leaf has no behavior.
|
|
249
|
+
|
|
173
250
|
## Scope
|
|
174
251
|
|
|
175
|
-
No recipes
|
|
176
|
-
|
|
177
|
-
`cls.property`, …).
|
|
252
|
+
No recipes for the structural containers and rows; apply the classes directly
|
|
253
|
+
(or read them from `cls.toolstrip`, `cls.pane`, `cls.splitter`, `cls.inspector`,
|
|
254
|
+
`cls.property`, …). `ui.toolstrip()` and `ui.selectionbar()` exist only to
|
|
255
|
+
compose the variant/anchor modifiers. Pair the selection bar with the
|
|
256
|
+
cross-cutting
|
|
178
257
|
[`ui-sel`](./selection.md) states on the selected items themselves. Bronto styles
|
|
179
258
|
the chrome and wires the splitter affordance; the host owns hit-testing,
|
|
180
259
|
persistence, pane contents, viewport semantics, and commands.
|
package/llms.txt
CHANGED
|
@@ -45,7 +45,7 @@ the path changes from source `css/` to built `dist/css/`:
|
|
|
45
45
|
<!-- installed locally -->
|
|
46
46
|
<link rel="stylesheet" href="./node_modules/@ponchia/ui/dist/css/<leaf>.css" />
|
|
47
47
|
<!-- or from a CDN; pin the version (pre-1.0, breaking changes ship in the minor) -->
|
|
48
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ponchia/ui@0.
|
|
48
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ponchia/ui@0.8.1/dist/css/<leaf>.css" />
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
The flattened default bundle is `dist/bronto.css` (bundler shorthand
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ponchia/ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "CSS-first identity and UI layer for services, tools, sites, and reports — works in HTML, every framework, and PDF, no component runtime. Shared app shell, forms, tables, workflow chrome, plus opt-in analytical/report primitives. Monochrome with one rationed accent. Zero runtime dependencies.",
|
|
6
6
|
"keywords": [
|
|
@@ -113,6 +113,7 @@
|
|
|
113
113
|
"docs/migrations/0.4-to-0.5.md",
|
|
114
114
|
"docs/migrations/0.5-to-0.6.md",
|
|
115
115
|
"docs/migrations/0.6-to-0.7.md",
|
|
116
|
+
"docs/migrations/0.7-to-0.8.md",
|
|
116
117
|
"docs/adr/0001-color-system.md",
|
|
117
118
|
"docs/adr/0002-scope-and-2026-baseline.md",
|
|
118
119
|
"docs/adr/0003-theme-model.md",
|
|
@@ -213,24 +214,24 @@
|
|
|
213
214
|
"prepublishOnly": "npm run build:artifacts && npm run check"
|
|
214
215
|
},
|
|
215
216
|
"devDependencies": {
|
|
216
|
-
"@arethetypeswrong/cli": "^0.18.
|
|
217
|
+
"@arethetypeswrong/cli": "^0.18.5",
|
|
217
218
|
"@axe-core/playwright": "^4.11.3",
|
|
218
219
|
"@builder.io/qwik": "^1.20.0",
|
|
219
220
|
"@playwright/test": "1.60.0",
|
|
220
221
|
"github-actionlint": "^1.7.12",
|
|
221
|
-
"jsdom": "^
|
|
222
|
-
"knip": "^6.
|
|
223
|
-
"pdfjs-dist": "^6.
|
|
224
|
-
"prettier": "^3.9.
|
|
225
|
-
"publint": "^0.3.
|
|
226
|
-
"react": "^19.2.
|
|
227
|
-
"react-dom": "^19.2.
|
|
228
|
-
"solid-js": "^1.9.
|
|
229
|
-
"stylelint": "^17.14.
|
|
222
|
+
"jsdom": "^30.0.1",
|
|
223
|
+
"knip": "^6.32.1",
|
|
224
|
+
"pdfjs-dist": "^6.2.108",
|
|
225
|
+
"prettier": "^3.9.6",
|
|
226
|
+
"publint": "^0.3.23",
|
|
227
|
+
"react": "^19.2.8",
|
|
228
|
+
"react-dom": "^19.2.8",
|
|
229
|
+
"solid-js": "^1.9.14",
|
|
230
|
+
"stylelint": "^17.14.1",
|
|
230
231
|
"stylelint-config-standard": "^40.0.0",
|
|
231
232
|
"stylelint-use-logical": "^2.1.3",
|
|
232
233
|
"typescript": "^6.0.3",
|
|
233
|
-
"vega": "^6.
|
|
234
|
+
"vega": "^6.3.1",
|
|
234
235
|
"vega-lite": "^6.4.3"
|
|
235
236
|
},
|
|
236
237
|
"peerDependencies": {
|
|
@@ -1906,6 +1906,62 @@
|
|
|
1906
1906
|
},
|
|
1907
1907
|
"unit": "px"
|
|
1908
1908
|
},
|
|
1909
|
+
{
|
|
1910
|
+
"name": "safe/area/bottom",
|
|
1911
|
+
"type": "STRING",
|
|
1912
|
+
"sourceCssVariable": "--safe-area-bottom",
|
|
1913
|
+
"valuesByMode": {
|
|
1914
|
+
"global": "env(safe-area-inset-bottom, 0px)"
|
|
1915
|
+
},
|
|
1916
|
+
"$extensions": {
|
|
1917
|
+
"com.ponchia.css": {
|
|
1918
|
+
"variable": "--safe-area-bottom",
|
|
1919
|
+
"value": "env(safe-area-inset-bottom, 0px)"
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
},
|
|
1923
|
+
{
|
|
1924
|
+
"name": "safe/area/left",
|
|
1925
|
+
"type": "STRING",
|
|
1926
|
+
"sourceCssVariable": "--safe-area-left",
|
|
1927
|
+
"valuesByMode": {
|
|
1928
|
+
"global": "env(safe-area-inset-left, 0px)"
|
|
1929
|
+
},
|
|
1930
|
+
"$extensions": {
|
|
1931
|
+
"com.ponchia.css": {
|
|
1932
|
+
"variable": "--safe-area-left",
|
|
1933
|
+
"value": "env(safe-area-inset-left, 0px)"
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
},
|
|
1937
|
+
{
|
|
1938
|
+
"name": "safe/area/right",
|
|
1939
|
+
"type": "STRING",
|
|
1940
|
+
"sourceCssVariable": "--safe-area-right",
|
|
1941
|
+
"valuesByMode": {
|
|
1942
|
+
"global": "env(safe-area-inset-right, 0px)"
|
|
1943
|
+
},
|
|
1944
|
+
"$extensions": {
|
|
1945
|
+
"com.ponchia.css": {
|
|
1946
|
+
"variable": "--safe-area-right",
|
|
1947
|
+
"value": "env(safe-area-inset-right, 0px)"
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1950
|
+
},
|
|
1951
|
+
{
|
|
1952
|
+
"name": "safe/area/top",
|
|
1953
|
+
"type": "STRING",
|
|
1954
|
+
"sourceCssVariable": "--safe-area-top",
|
|
1955
|
+
"valuesByMode": {
|
|
1956
|
+
"global": "env(safe-area-inset-top, 0px)"
|
|
1957
|
+
},
|
|
1958
|
+
"$extensions": {
|
|
1959
|
+
"com.ponchia.css": {
|
|
1960
|
+
"variable": "--safe-area-top",
|
|
1961
|
+
"value": "env(safe-area-inset-top, 0px)"
|
|
1962
|
+
}
|
|
1963
|
+
}
|
|
1964
|
+
},
|
|
1909
1965
|
{
|
|
1910
1966
|
"name": "sans",
|
|
1911
1967
|
"type": "STRING",
|
|
@@ -2025,6 +2081,34 @@
|
|
|
2025
2081
|
},
|
|
2026
2082
|
"unit": "rem"
|
|
2027
2083
|
},
|
|
2084
|
+
{
|
|
2085
|
+
"name": "tap/target",
|
|
2086
|
+
"type": "STRING",
|
|
2087
|
+
"sourceCssVariable": "--tap-target",
|
|
2088
|
+
"valuesByMode": {
|
|
2089
|
+
"global": "max(44px, 2.9rem)"
|
|
2090
|
+
},
|
|
2091
|
+
"$extensions": {
|
|
2092
|
+
"com.ponchia.css": {
|
|
2093
|
+
"variable": "--tap-target",
|
|
2094
|
+
"value": "max(44px, 2.9rem)"
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
},
|
|
2098
|
+
{
|
|
2099
|
+
"name": "tap/target/min",
|
|
2100
|
+
"type": "STRING",
|
|
2101
|
+
"sourceCssVariable": "--tap-target-min",
|
|
2102
|
+
"valuesByMode": {
|
|
2103
|
+
"global": "max(24px, 1.6rem)"
|
|
2104
|
+
},
|
|
2105
|
+
"$extensions": {
|
|
2106
|
+
"com.ponchia.css": {
|
|
2107
|
+
"variable": "--tap-target-min",
|
|
2108
|
+
"value": "max(24px, 1.6rem)"
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
},
|
|
2028
2112
|
{
|
|
2029
2113
|
"name": "text/2xs",
|
|
2030
2114
|
"type": "FLOAT",
|
package/tokens/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
export type ThemeName = 'light' | 'dark';
|
|
5
5
|
|
|
6
|
-
export type GlobalTokenName = '--radius-xl' | '--radius-lg' | '--radius-md' | '--radius-sm' | '--radius-pill' | '--space-2xs' | '--space-xs' | '--space-sm' | '--space-md' | '--space-lg' | '--space-xl' | '--space-2xl' | '--mono' | '--sans' | '--dot-font' | '--display' | '--display-weight' | '--display-weight-strong' | '--text-2xs' | '--text-xs' | '--text-sm' | '--text-base' | '--text-lg' | '--text-xl' | '--tracking-wide' | '--tracking-wider' | '--ease-standard' | '--ease-spring' | '--ease-out' | '--duration-fast' | '--duration-base' | '--duration-slow' | '--dot-size' | '--dot-gap' | '--z-base' | '--z-raised' | '--z-sticky' | '--z-overlay' | '--z-popover' | '--z-toast' | '--accent-1' | '--accent-2' | '--accent-3' | '--accent-4' | '--accent-5' | '--accent-6' | '--surface-1' | '--surface-2' | '--surface-3' | '--surface-4' | '--surface-5' | '--surface-6' | '--bronto-color-bg' | '--bronto-color-surface' | '--bronto-color-surface-raised' | '--bronto-color-border' | '--bronto-color-border-strong' | '--bronto-color-text' | '--bronto-color-text-muted' | '--bronto-color-action' | '--bronto-color-on-action' | '--bronto-color-focus' | '--bronto-color-success' | '--bronto-color-warning' | '--bronto-color-danger' | '--bronto-color-info' | '--surface' | '--surface-raised' | '--surface-muted' | '--border' | '--border-strong';
|
|
6
|
+
export type GlobalTokenName = '--radius-xl' | '--radius-lg' | '--radius-md' | '--radius-sm' | '--radius-pill' | '--space-2xs' | '--space-xs' | '--space-sm' | '--space-md' | '--space-lg' | '--space-xl' | '--space-2xl' | '--tap-target' | '--tap-target-min' | '--safe-area-top' | '--safe-area-right' | '--safe-area-bottom' | '--safe-area-left' | '--mono' | '--sans' | '--dot-font' | '--display' | '--display-weight' | '--display-weight-strong' | '--text-2xs' | '--text-xs' | '--text-sm' | '--text-base' | '--text-lg' | '--text-xl' | '--tracking-wide' | '--tracking-wider' | '--ease-standard' | '--ease-spring' | '--ease-out' | '--duration-fast' | '--duration-base' | '--duration-slow' | '--dot-size' | '--dot-gap' | '--z-base' | '--z-raised' | '--z-sticky' | '--z-overlay' | '--z-popover' | '--z-toast' | '--accent-1' | '--accent-2' | '--accent-3' | '--accent-4' | '--accent-5' | '--accent-6' | '--surface-1' | '--surface-2' | '--surface-3' | '--surface-4' | '--surface-5' | '--surface-6' | '--bronto-color-bg' | '--bronto-color-surface' | '--bronto-color-surface-raised' | '--bronto-color-border' | '--bronto-color-border-strong' | '--bronto-color-text' | '--bronto-color-text-muted' | '--bronto-color-action' | '--bronto-color-on-action' | '--bronto-color-focus' | '--bronto-color-success' | '--bronto-color-warning' | '--bronto-color-danger' | '--bronto-color-info' | '--surface' | '--surface-raised' | '--surface-muted' | '--border' | '--border-strong';
|
|
7
7
|
export type LightTokenName = '--bg' | '--bg-elevated' | '--bg-accent' | '--panel' | '--panel-strong' | '--panel-soft' | '--line' | '--line-strong' | '--text' | '--text-soft' | '--text-dim' | '--accent' | '--accent-ramp-end' | '--accent-strong' | '--accent-text' | '--on-accent' | '--accent-soft' | '--success' | '--success-soft' | '--warning' | '--warning-soft' | '--danger' | '--danger-soft' | '--info' | '--info-soft' | '--code-bg' | '--button-text' | '--field-dot' | '--field-dot-hot' | '--field-dot-accent' | '--focus-ring' | '--shadow' | '--shadow-raised';
|
|
8
8
|
export type DarkTokenName = '--bg' | '--bg-elevated' | '--bg-accent' | '--panel' | '--panel-strong' | '--panel-soft' | '--line' | '--line-strong' | '--text' | '--text-soft' | '--text-dim' | '--accent' | '--accent-ramp-end' | '--accent-strong' | '--accent-text' | '--on-accent' | '--accent-soft' | '--success' | '--success-soft' | '--warning' | '--warning-soft' | '--danger' | '--danger-soft' | '--info' | '--info-soft' | '--code-bg' | '--button-text' | '--field-dot' | '--field-dot-hot' | '--field-dot-accent' | '--focus-ring' | '--shadow' | '--shadow-raised';
|
|
9
9
|
|
|
@@ -14,7 +14,7 @@ export declare const cssVars: {
|
|
|
14
14
|
dark: Record<DarkTokenName, string>;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
export type ScaleKey = 'radius-xl' | 'radius-lg' | 'radius-md' | 'radius-sm' | 'radius-pill' | 'space-2xs' | 'space-xs' | 'space-sm' | 'space-md' | 'space-lg' | 'space-xl' | 'space-2xl' | 'mono' | 'sans' | 'dot-font' | 'display' | 'display-weight' | 'display-weight-strong' | 'text-2xs' | 'text-xs' | 'text-sm' | 'text-base' | 'text-lg' | 'text-xl' | 'tracking-wide' | 'tracking-wider' | 'ease-standard' | 'ease-spring' | 'ease-out' | 'duration-fast' | 'duration-base' | 'duration-slow' | 'dot-size' | 'dot-gap' | 'z-base' | 'z-raised' | 'z-sticky' | 'z-overlay' | 'z-popover' | 'z-toast' | 'accent-1' | 'accent-2' | 'accent-3' | 'accent-4' | 'accent-5' | 'accent-6' | 'surface-1' | 'surface-2' | 'surface-3' | 'surface-4' | 'surface-5' | 'surface-6' | 'bronto-color-bg' | 'bronto-color-surface' | 'bronto-color-surface-raised' | 'bronto-color-border' | 'bronto-color-border-strong' | 'bronto-color-text' | 'bronto-color-text-muted' | 'bronto-color-action' | 'bronto-color-on-action' | 'bronto-color-focus' | 'bronto-color-success' | 'bronto-color-warning' | 'bronto-color-danger' | 'bronto-color-info' | 'surface' | 'surface-raised' | 'surface-muted' | 'border' | 'border-strong';
|
|
17
|
+
export type ScaleKey = 'radius-xl' | 'radius-lg' | 'radius-md' | 'radius-sm' | 'radius-pill' | 'space-2xs' | 'space-xs' | 'space-sm' | 'space-md' | 'space-lg' | 'space-xl' | 'space-2xl' | 'tap-target' | 'tap-target-min' | 'safe-area-top' | 'safe-area-right' | 'safe-area-bottom' | 'safe-area-left' | 'mono' | 'sans' | 'dot-font' | 'display' | 'display-weight' | 'display-weight-strong' | 'text-2xs' | 'text-xs' | 'text-sm' | 'text-base' | 'text-lg' | 'text-xl' | 'tracking-wide' | 'tracking-wider' | 'ease-standard' | 'ease-spring' | 'ease-out' | 'duration-fast' | 'duration-base' | 'duration-slow' | 'dot-size' | 'dot-gap' | 'z-base' | 'z-raised' | 'z-sticky' | 'z-overlay' | 'z-popover' | 'z-toast' | 'accent-1' | 'accent-2' | 'accent-3' | 'accent-4' | 'accent-5' | 'accent-6' | 'surface-1' | 'surface-2' | 'surface-3' | 'surface-4' | 'surface-5' | 'surface-6' | 'bronto-color-bg' | 'bronto-color-surface' | 'bronto-color-surface-raised' | 'bronto-color-border' | 'bronto-color-border-strong' | 'bronto-color-text' | 'bronto-color-text-muted' | 'bronto-color-action' | 'bronto-color-on-action' | 'bronto-color-focus' | 'bronto-color-success' | 'bronto-color-warning' | 'bronto-color-danger' | 'bronto-color-info' | 'surface' | 'surface-raised' | 'surface-muted' | 'border' | 'border-strong';
|
|
18
18
|
export type ColorKey = 'bg' | 'bg-elevated' | 'bg-accent' | 'panel' | 'panel-strong' | 'panel-soft' | 'line' | 'line-strong' | 'text' | 'text-soft' | 'text-dim' | 'accent' | 'accent-ramp-end' | 'accent-strong' | 'accent-text' | 'on-accent' | 'accent-soft' | 'success' | 'success-soft' | 'warning' | 'warning-soft' | 'danger' | 'danger-soft' | 'info' | 'info-soft' | 'code-bg' | 'button-text' | 'field-dot' | 'field-dot-hot' | 'field-dot-accent' | 'focus-ring' | 'shadow' | 'shadow-raised';
|
|
19
19
|
|
|
20
20
|
/** Ergonomic view derived from {@link cssVars} (`--` prefix stripped). */
|
package/tokens/index.js
CHANGED
|
@@ -33,6 +33,29 @@ export const cssVars = {
|
|
|
33
33
|
'--space-lg': '1.35rem',
|
|
34
34
|
'--space-xl': '1.75rem',
|
|
35
35
|
'--space-2xl': '2.5rem',
|
|
36
|
+
// Tap targets. Both floors are clamped in px against the rem so they cannot
|
|
37
|
+
// shrink below the standard when a host re-points the root font size —
|
|
38
|
+
// Bronto's own base sets `html { font-size: 0.9375rem }`, under which a bare
|
|
39
|
+
// 2.9rem is 43.5px, half a pixel short of the target floor.
|
|
40
|
+
// `--tap-target` is the WCAG 2.5.5 (AAA) / iOS-HIG / Material 44px target
|
|
41
|
+
// every coarse-pointer control floats to. `--tap-target-min` is the WCAG
|
|
42
|
+
// 2.5.8 (AA) 24px minimum, for controls that only have to clear the smaller
|
|
43
|
+
// bar (standalone CTA links, disclosure carets).
|
|
44
|
+
'--tap-target': 'max(44px, 2.9rem)',
|
|
45
|
+
'--tap-target-min': 'max(24px, 1.6rem)',
|
|
46
|
+
// Display cutouts and system gesture areas. Every viewport-anchored surface
|
|
47
|
+
// Bronto ships — the app rail and topbar, a sticky site header, the skip
|
|
48
|
+
// link, the toast stacks, the drawer and the lightbox — reads these instead
|
|
49
|
+
// of calling env() at the point of use. The indirection is the point: env()
|
|
50
|
+
// cannot be emulated by a desktop browser runner, so routing every inset
|
|
51
|
+
// through an overrideable custom property is what makes full-bleed framing
|
|
52
|
+
// testable, and what lets a host running inside its own chrome (an embedded
|
|
53
|
+
// webview, a kiosk frame) declare the real insets. They resolve to 0px off
|
|
54
|
+
// a notched device, so every consuming rule is a no-op on desktop.
|
|
55
|
+
'--safe-area-top': 'env(safe-area-inset-top, 0px)',
|
|
56
|
+
'--safe-area-right': 'env(safe-area-inset-right, 0px)',
|
|
57
|
+
'--safe-area-bottom': 'env(safe-area-inset-bottom, 0px)',
|
|
58
|
+
'--safe-area-left': 'env(safe-area-inset-left, 0px)',
|
|
36
59
|
'--mono': "'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', ui-monospace, monospace",
|
|
37
60
|
'--sans':
|
|
38
61
|
"'Inter', 'SF Pro Text', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif",
|
package/tokens/index.json
CHANGED
|
@@ -14,6 +14,12 @@
|
|
|
14
14
|
"--space-lg": "1.35rem",
|
|
15
15
|
"--space-xl": "1.75rem",
|
|
16
16
|
"--space-2xl": "2.5rem",
|
|
17
|
+
"--tap-target": "max(44px, 2.9rem)",
|
|
18
|
+
"--tap-target-min": "max(24px, 1.6rem)",
|
|
19
|
+
"--safe-area-top": "env(safe-area-inset-top, 0px)",
|
|
20
|
+
"--safe-area-right": "env(safe-area-inset-right, 0px)",
|
|
21
|
+
"--safe-area-bottom": "env(safe-area-inset-bottom, 0px)",
|
|
22
|
+
"--safe-area-left": "env(safe-area-inset-left, 0px)",
|
|
17
23
|
"--mono": "'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', ui-monospace, monospace",
|
|
18
24
|
"--sans": "'Inter', 'SF Pro Text', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif",
|
|
19
25
|
"--dot-font": "'Doto', var(--mono)",
|
|
@@ -159,6 +165,12 @@
|
|
|
159
165
|
"space-lg": "1.35rem",
|
|
160
166
|
"space-xl": "1.75rem",
|
|
161
167
|
"space-2xl": "2.5rem",
|
|
168
|
+
"tap-target": "max(44px, 2.9rem)",
|
|
169
|
+
"tap-target-min": "max(24px, 1.6rem)",
|
|
170
|
+
"safe-area-top": "env(safe-area-inset-top, 0px)",
|
|
171
|
+
"safe-area-right": "env(safe-area-inset-right, 0px)",
|
|
172
|
+
"safe-area-bottom": "env(safe-area-inset-bottom, 0px)",
|
|
173
|
+
"safe-area-left": "env(safe-area-inset-left, 0px)",
|
|
162
174
|
"mono": "'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', ui-monospace, monospace",
|
|
163
175
|
"sans": "'Inter', 'SF Pro Text', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif",
|
|
164
176
|
"dot-font": "'Doto', var(--mono)",
|
package/tokens/resolved.json
CHANGED
|
@@ -141,6 +141,12 @@
|
|
|
141
141
|
"--space-lg": "1.35rem",
|
|
142
142
|
"--space-xl": "1.75rem",
|
|
143
143
|
"--space-2xl": "2.5rem",
|
|
144
|
+
"--tap-target": "max(44px, 2.9rem)",
|
|
145
|
+
"--tap-target-min": "max(24px, 1.6rem)",
|
|
146
|
+
"--safe-area-top": "env(safe-area-inset-top, 0px)",
|
|
147
|
+
"--safe-area-right": "env(safe-area-inset-right, 0px)",
|
|
148
|
+
"--safe-area-bottom": "env(safe-area-inset-bottom, 0px)",
|
|
149
|
+
"--safe-area-left": "env(safe-area-inset-left, 0px)",
|
|
144
150
|
"--mono": "'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', ui-monospace, monospace",
|
|
145
151
|
"--sans": "'Inter', 'SF Pro Text', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif",
|
|
146
152
|
"--dot-font": "'Doto', 'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', ui-monospace, monospace",
|