@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/css/report.css
CHANGED
|
@@ -619,46 +619,6 @@
|
|
|
619
619
|
text-transform: uppercase;
|
|
620
620
|
}
|
|
621
621
|
|
|
622
|
-
/* --- Labelled meter row ---
|
|
623
|
-
A multi-meter block (SLO burn, error budgets, capacity) lays out as
|
|
624
|
-
label | bar | value. The bare `ui-meter` base lives in feedback.css; this is
|
|
625
|
-
the report-document grammar around it so authors stop hand-rolling the grid.
|
|
626
|
-
The bar NEVER carries the reading alone (WCAG 1.4.1) — `ui-meter__value` is
|
|
627
|
-
the data of record, and the bar clamps at 100 so an over-target figure still
|
|
628
|
-
reads correctly in the value text. Collapses to a stack on a narrow screen. */
|
|
629
|
-
.ui-meter__row {
|
|
630
|
-
align-items: center;
|
|
631
|
-
display: grid;
|
|
632
|
-
gap: var(--space-2xs) var(--space-md);
|
|
633
|
-
grid-template-columns: minmax(9rem, 14rem) 1fr auto;
|
|
634
|
-
margin-block: var(--space-2xs);
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
.ui-meter__row .ui-meter {
|
|
638
|
-
min-inline-size: 8rem;
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
.ui-meter__label {
|
|
642
|
-
color: var(--text-soft);
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
.ui-meter__value {
|
|
646
|
-
color: var(--text);
|
|
647
|
-
font-family: var(--mono);
|
|
648
|
-
font-variant-numeric: tabular-nums;
|
|
649
|
-
text-align: end;
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
@media (max-width: 32rem) {
|
|
653
|
-
.ui-meter__row {
|
|
654
|
-
grid-template-columns: 1fr;
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
.ui-meter__value {
|
|
658
|
-
text-align: start;
|
|
659
|
-
}
|
|
660
|
-
}
|
|
661
|
-
|
|
662
622
|
/* A chart is NOT a bronto component — it needs scales + data binding, which the
|
|
663
623
|
analytical layer refuses to own. Theme Vega-Lite with `@ponchia/ui/vega`
|
|
664
624
|
(docs/vega.md), or hand-author a token-themed inline `<svg>`, and drop it in a
|
package/css/site.css
CHANGED
|
@@ -41,8 +41,10 @@
|
|
|
41
41
|
z-index: var(--z-toast);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/* The skip link is the FIRST thing a keyboard or screen-reader user reaches, so
|
|
45
|
+
landing it under a display cutout is the worst place to lose a control. */
|
|
44
46
|
.ui-skiplink:focus {
|
|
45
|
-
inset-block-start: var(--space-md);
|
|
47
|
+
inset-block-start: max(var(--space-md), var(--safe-area-top));
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
/* --- Header — brand · nav · actions --- */
|
|
@@ -60,6 +62,10 @@
|
|
|
60
62
|
/* Structural sticky only — the floating-card skin (radius/shadow/panel
|
|
61
63
|
bg) is consumer identity, not a framework concern; layer it yourself. */
|
|
62
64
|
.ui-siteheader--sticky {
|
|
65
|
+
/* Only the sticky variant needs this: a header in normal flow already sits
|
|
66
|
+
below the cutout. Pad rather than offset, so the header's own border and
|
|
67
|
+
any consumer background still reach the top of the screen. */
|
|
68
|
+
padding-block-start: max(var(--space-sm), var(--safe-area-top));
|
|
63
69
|
position: sticky;
|
|
64
70
|
inset-block-start: 0;
|
|
65
71
|
z-index: var(--z-sticky);
|
|
@@ -184,7 +190,7 @@
|
|
|
184
190
|
|
|
185
191
|
@media (pointer: coarse) {
|
|
186
192
|
.ui-sitenav a {
|
|
187
|
-
min-block-size:
|
|
193
|
+
min-block-size: var(--tap-target);
|
|
188
194
|
padding-inline: 0.9rem;
|
|
189
195
|
}
|
|
190
196
|
}
|
|
@@ -256,7 +262,7 @@
|
|
|
256
262
|
|
|
257
263
|
@media (pointer: coarse) {
|
|
258
264
|
.ui-sitemenu > summary {
|
|
259
|
-
min-block-size:
|
|
265
|
+
min-block-size: var(--tap-target);
|
|
260
266
|
padding-inline: 0.9rem;
|
|
261
267
|
}
|
|
262
268
|
}
|
|
@@ -296,6 +302,16 @@
|
|
|
296
302
|
}
|
|
297
303
|
}
|
|
298
304
|
|
|
305
|
+
@media (pointer: coarse) {
|
|
306
|
+
.ui-sitefooter__links a {
|
|
307
|
+
align-items: center;
|
|
308
|
+
display: inline-flex;
|
|
309
|
+
justify-content: center;
|
|
310
|
+
min-block-size: var(--tap-target-min);
|
|
311
|
+
min-inline-size: var(--tap-target-min);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
299
315
|
/* --- Tags — neutral, wrapping content labels (NOT ui-chip, which is a
|
|
300
316
|
single interactive token). Use a <ul>. --- */
|
|
301
317
|
|
package/css/skins.css
CHANGED
|
@@ -4,43 +4,107 @@
|
|
|
4
4
|
* Optional display colorways (ADR-0001). OPT-IN: imported on demand via
|
|
5
5
|
* `@ponchia/ui/css/skins.css`, never part of the default bundle. Apply with
|
|
6
6
|
* `data-bronto-skin="amber-crt | e-ink | phosphor-green"` on :root / <html> (a
|
|
7
|
-
* root-level choice like data-theme), re-pointing the one accent
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* root-level choice like data-theme), re-pointing the one accent and the
|
|
8
|
+
* neutral canvas (ADR-0001 step 4, amended in 0.8.0). The accent's derived
|
|
9
|
+
* family recomputes from the live var(--accent); STATUS COLOURS ARE UNTOUCHED,
|
|
10
|
+
* because a warning must look like a warning in every skin. The canvas keeps
|
|
11
|
+
* each core neutral's OKLCH lightness and only moves its hue, so contrast is
|
|
12
|
+
* preserved by construction — and check-contrast.mjs re-measures the FULL
|
|
13
|
+
* pairing table per skin per theme rather than trusting that. */
|
|
10
14
|
|
|
11
15
|
/* Amber CRT */
|
|
12
16
|
:root[data-bronto-skin='amber-crt'] {
|
|
13
17
|
--accent: oklch(52% 0.11 67deg);
|
|
14
18
|
--dotmatrix-pulse-min: 0.35;
|
|
19
|
+
--bg: oklch(96.66% 0.02 67deg);
|
|
20
|
+
--bg-elevated: oklch(98.781% 0.022 67deg);
|
|
21
|
+
--panel: oklch(100% 0.024 67deg);
|
|
22
|
+
--panel-strong: oklch(100% 0.026 67deg);
|
|
23
|
+
--panel-soft: oklch(94.253% 0.026 67deg);
|
|
24
|
+
--line: oklch(88.11% 0.035 67deg);
|
|
25
|
+
--line-strong: oklch(72.983% 0.04 67deg);
|
|
26
|
+
--text: oklch(14.479% 0.03 67deg);
|
|
27
|
+
--text-soft: oklch(32.825% 0.03 67deg);
|
|
28
|
+
--text-dim: oklch(51.571% 0.028 67deg);
|
|
15
29
|
}
|
|
16
30
|
|
|
17
31
|
:root[data-theme='dark'][data-bronto-skin='amber-crt'] {
|
|
18
32
|
--accent: oklch(82% 0.15 82deg);
|
|
19
33
|
--dotmatrix-glow: 0.4em;
|
|
20
34
|
--dotmatrix-pulse-min: 0.3;
|
|
35
|
+
--bg: oklch(18.22% 0.02 82deg);
|
|
36
|
+
--bg-elevated: oklch(20.904% 0.022 82deg);
|
|
37
|
+
--panel: oklch(22.645% 0.024 82deg);
|
|
38
|
+
--panel-strong: oklch(25.196% 0.026 82deg);
|
|
39
|
+
--panel-soft: oklch(26.032% 0.026 82deg);
|
|
40
|
+
--line: oklch(34.07% 0.035 82deg);
|
|
41
|
+
--line-strong: oklch(44.953% 0.04 82deg);
|
|
42
|
+
--text: oklch(92.494% 0.03 82deg);
|
|
43
|
+
--text-soft: oklch(83.279% 0.03 82deg);
|
|
44
|
+
--text-dim: oklch(70.576% 0.028 82deg);
|
|
21
45
|
}
|
|
22
46
|
|
|
23
47
|
/* E-ink */
|
|
24
48
|
:root[data-bronto-skin='e-ink'] {
|
|
25
49
|
--accent: oklch(34% 0.012 250deg);
|
|
26
50
|
--dotmatrix-reveal-step: 0ms;
|
|
51
|
+
--bg: oklch(96.66% 0.005 250deg);
|
|
52
|
+
--bg-elevated: oklch(98.781% 0.006 250deg);
|
|
53
|
+
--panel: oklch(100% 0.006 250deg);
|
|
54
|
+
--panel-strong: oklch(100% 0.007 250deg);
|
|
55
|
+
--panel-soft: oklch(94.253% 0.007 250deg);
|
|
56
|
+
--line: oklch(88.11% 0.009 250deg);
|
|
57
|
+
--line-strong: oklch(72.983% 0.01 250deg);
|
|
58
|
+
--text: oklch(14.479% 0.008 250deg);
|
|
59
|
+
--text-soft: oklch(32.825% 0.008 250deg);
|
|
60
|
+
--text-dim: oklch(51.571% 0.007 250deg);
|
|
27
61
|
}
|
|
28
62
|
|
|
29
63
|
:root[data-theme='dark'][data-bronto-skin='e-ink'] {
|
|
30
64
|
--accent: oklch(84% 0.008 250deg);
|
|
31
65
|
--dotmatrix-reveal-step: 0ms;
|
|
66
|
+
--bg: oklch(18.22% 0.005 250deg);
|
|
67
|
+
--bg-elevated: oklch(20.904% 0.006 250deg);
|
|
68
|
+
--panel: oklch(22.645% 0.006 250deg);
|
|
69
|
+
--panel-strong: oklch(25.196% 0.007 250deg);
|
|
70
|
+
--panel-soft: oklch(26.032% 0.007 250deg);
|
|
71
|
+
--line: oklch(34.07% 0.009 250deg);
|
|
72
|
+
--line-strong: oklch(44.953% 0.01 250deg);
|
|
73
|
+
--text: oklch(92.494% 0.008 250deg);
|
|
74
|
+
--text-soft: oklch(83.279% 0.008 250deg);
|
|
75
|
+
--text-dim: oklch(70.576% 0.007 250deg);
|
|
32
76
|
}
|
|
33
77
|
|
|
34
78
|
/* Phosphor Green */
|
|
35
79
|
:root[data-bronto-skin='phosphor-green'] {
|
|
36
80
|
--accent: oklch(52% 0.13 150deg);
|
|
37
81
|
--dotmatrix-pulse-min: 0.35;
|
|
82
|
+
--bg: oklch(96.66% 0.02 150deg);
|
|
83
|
+
--bg-elevated: oklch(98.781% 0.022 150deg);
|
|
84
|
+
--panel: oklch(100% 0.024 150deg);
|
|
85
|
+
--panel-strong: oklch(100% 0.026 150deg);
|
|
86
|
+
--panel-soft: oklch(94.253% 0.026 150deg);
|
|
87
|
+
--line: oklch(88.11% 0.035 150deg);
|
|
88
|
+
--line-strong: oklch(72.983% 0.04 150deg);
|
|
89
|
+
--text: oklch(14.479% 0.03 150deg);
|
|
90
|
+
--text-soft: oklch(32.825% 0.03 150deg);
|
|
91
|
+
--text-dim: oklch(51.571% 0.028 150deg);
|
|
38
92
|
}
|
|
39
93
|
|
|
40
94
|
:root[data-theme='dark'][data-bronto-skin='phosphor-green'] {
|
|
41
95
|
--accent: oklch(84% 0.19 150deg);
|
|
42
96
|
--dotmatrix-glow: 0.4em;
|
|
43
97
|
--dotmatrix-pulse-min: 0.3;
|
|
98
|
+
--bg: oklch(18.22% 0.02 150deg);
|
|
99
|
+
--bg-elevated: oklch(20.904% 0.022 150deg);
|
|
100
|
+
--panel: oklch(22.645% 0.024 150deg);
|
|
101
|
+
--panel-strong: oklch(25.196% 0.026 150deg);
|
|
102
|
+
--panel-soft: oklch(26.032% 0.026 150deg);
|
|
103
|
+
--line: oklch(34.07% 0.035 150deg);
|
|
104
|
+
--line-strong: oklch(44.953% 0.04 150deg);
|
|
105
|
+
--text: oklch(92.494% 0.03 150deg);
|
|
106
|
+
--text-soft: oklch(83.279% 0.03 150deg);
|
|
107
|
+
--text-dim: oklch(70.576% 0.028 150deg);
|
|
44
108
|
}
|
|
45
109
|
|
|
46
110
|
@media (prefers-color-scheme: dark) {
|
|
@@ -48,16 +112,46 @@
|
|
|
48
112
|
--accent: oklch(82% 0.15 82deg);
|
|
49
113
|
--dotmatrix-glow: 0.4em;
|
|
50
114
|
--dotmatrix-pulse-min: 0.3;
|
|
115
|
+
--bg: oklch(18.22% 0.02 82deg);
|
|
116
|
+
--bg-elevated: oklch(20.904% 0.022 82deg);
|
|
117
|
+
--panel: oklch(22.645% 0.024 82deg);
|
|
118
|
+
--panel-strong: oklch(25.196% 0.026 82deg);
|
|
119
|
+
--panel-soft: oklch(26.032% 0.026 82deg);
|
|
120
|
+
--line: oklch(34.07% 0.035 82deg);
|
|
121
|
+
--line-strong: oklch(44.953% 0.04 82deg);
|
|
122
|
+
--text: oklch(92.494% 0.03 82deg);
|
|
123
|
+
--text-soft: oklch(83.279% 0.03 82deg);
|
|
124
|
+
--text-dim: oklch(70.576% 0.028 82deg);
|
|
51
125
|
}
|
|
52
126
|
|
|
53
127
|
:root:not([data-theme='light'])[data-bronto-skin='e-ink'] {
|
|
54
128
|
--accent: oklch(84% 0.008 250deg);
|
|
55
129
|
--dotmatrix-reveal-step: 0ms;
|
|
130
|
+
--bg: oklch(18.22% 0.005 250deg);
|
|
131
|
+
--bg-elevated: oklch(20.904% 0.006 250deg);
|
|
132
|
+
--panel: oklch(22.645% 0.006 250deg);
|
|
133
|
+
--panel-strong: oklch(25.196% 0.007 250deg);
|
|
134
|
+
--panel-soft: oklch(26.032% 0.007 250deg);
|
|
135
|
+
--line: oklch(34.07% 0.009 250deg);
|
|
136
|
+
--line-strong: oklch(44.953% 0.01 250deg);
|
|
137
|
+
--text: oklch(92.494% 0.008 250deg);
|
|
138
|
+
--text-soft: oklch(83.279% 0.008 250deg);
|
|
139
|
+
--text-dim: oklch(70.576% 0.007 250deg);
|
|
56
140
|
}
|
|
57
141
|
|
|
58
142
|
:root:not([data-theme='light'])[data-bronto-skin='phosphor-green'] {
|
|
59
143
|
--accent: oklch(84% 0.19 150deg);
|
|
60
144
|
--dotmatrix-glow: 0.4em;
|
|
61
145
|
--dotmatrix-pulse-min: 0.3;
|
|
146
|
+
--bg: oklch(18.22% 0.02 150deg);
|
|
147
|
+
--bg-elevated: oklch(20.904% 0.022 150deg);
|
|
148
|
+
--panel: oklch(22.645% 0.024 150deg);
|
|
149
|
+
--panel-strong: oklch(25.196% 0.026 150deg);
|
|
150
|
+
--panel-soft: oklch(26.032% 0.026 150deg);
|
|
151
|
+
--line: oklch(34.07% 0.035 150deg);
|
|
152
|
+
--line-strong: oklch(44.953% 0.04 150deg);
|
|
153
|
+
--text: oklch(92.494% 0.03 150deg);
|
|
154
|
+
--text-soft: oklch(83.279% 0.03 150deg);
|
|
155
|
+
--text-dim: oklch(70.576% 0.028 150deg);
|
|
62
156
|
}
|
|
63
157
|
}
|
package/css/state.css
CHANGED
|
@@ -223,6 +223,153 @@
|
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
225
|
|
|
226
|
+
/* ==========================================================================
|
|
227
|
+
Severity — the second axis of system state.
|
|
228
|
+
|
|
229
|
+
`.ui-state` answers "what is this thing doing". This answers "how bad is
|
|
230
|
+
it". Bronto already shipped the TONES (`--danger`, `--warning`, `--info`,
|
|
231
|
+
`--success`) as per-component modifiers, but never the SCALE: the tier names,
|
|
232
|
+
their order, and the attribute that carries them. So every consumer invents
|
|
233
|
+
the ladder, and inside a single app it drifts — one surface saying
|
|
234
|
+
`critical|error|warning|note`, the next `bad|warn`, a third
|
|
235
|
+
`critical|warning|info|ok`, under two different attribute names. Findings
|
|
236
|
+
then do not sort against alerts, and a filter written for one list silently
|
|
237
|
+
misses the other.
|
|
238
|
+
|
|
239
|
+
THE LADDER, worst to best:
|
|
240
|
+
|
|
241
|
+
critical broken now, and still losing something
|
|
242
|
+
error something failed; it is not currently getting worse
|
|
243
|
+
warning a threshold was crossed; nothing has failed yet
|
|
244
|
+
notice worth reading, no action implied
|
|
245
|
+
ok checked and healthy — an ASSERTION, not the absence of news
|
|
246
|
+
unknown not measured, stale, or the check itself failed
|
|
247
|
+
|
|
248
|
+
`unknown` sits OUTSIDE the ordering deliberately. It is not "slightly worse
|
|
249
|
+
than ok", it is "we do not know", and collapsing it into `ok` is how a dead
|
|
250
|
+
collector reads as a healthy system.
|
|
251
|
+
|
|
252
|
+
The level is carried by `data-level` — one attribute name, so the same
|
|
253
|
+
selector works on a chip, a row, a dot, or the host's own element:
|
|
254
|
+
|
|
255
|
+
<span class="ui-severity" data-level="critical">Critical</span>
|
|
256
|
+
<li class="ui-severity-row" data-level="warning">…</li>
|
|
257
|
+
|
|
258
|
+
BOUNDARY: the host owns thresholds, ranking, filtering, and the wording of
|
|
259
|
+
every label. Colour is never the only channel (WCAG 1.4.1) — `.ui-severity`
|
|
260
|
+
carries an author-written label, and `.ui-severity-dot` is only for rows that
|
|
261
|
+
ALSO name their level in text.
|
|
262
|
+
========================================================================== */
|
|
263
|
+
|
|
264
|
+
/* Declared once so a host putting the level on its own element gets the same
|
|
265
|
+
mapping from `data-level` + `var(--severity-tone)`, without copying a table. */
|
|
266
|
+
.ui-severity,
|
|
267
|
+
.ui-severity-row,
|
|
268
|
+
.ui-severity-dot {
|
|
269
|
+
--severity-tone: var(--text-dim);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
:is(.ui-severity, .ui-severity-row, .ui-severity-dot):is(
|
|
273
|
+
[data-level='critical'],
|
|
274
|
+
[data-level='error']
|
|
275
|
+
) {
|
|
276
|
+
--severity-tone: var(--danger);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
:is(.ui-severity, .ui-severity-row, .ui-severity-dot)[data-level='warning'] {
|
|
280
|
+
--severity-tone: var(--warning);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
:is(.ui-severity, .ui-severity-row, .ui-severity-dot)[data-level='notice'] {
|
|
284
|
+
--severity-tone: var(--info);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
:is(.ui-severity, .ui-severity-row, .ui-severity-dot)[data-level='ok'] {
|
|
288
|
+
--severity-tone: var(--success);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/* `unknown` keeps the neutral default tone — it must not read as a verdict. */
|
|
292
|
+
|
|
293
|
+
/* The chip — a short uppercase tier label, tinted rather than filled, because
|
|
294
|
+
a list of findings is mostly chips and filled ones turn it into stripes.
|
|
295
|
+
`critical` is the exception and fills: in a scrolling list the worst tier has
|
|
296
|
+
to survive peripheral vision. */
|
|
297
|
+
.ui-severity {
|
|
298
|
+
background: color-mix(in oklch, var(--severity-tone) 16%, transparent);
|
|
299
|
+
border-radius: var(--radius-pill);
|
|
300
|
+
color: var(--severity-tone);
|
|
301
|
+
display: inline-block;
|
|
302
|
+
flex: none;
|
|
303
|
+
font-family: var(--mono);
|
|
304
|
+
font-size: var(--text-2xs);
|
|
305
|
+
font-weight: 700;
|
|
306
|
+
letter-spacing: var(--tracking-wide);
|
|
307
|
+
line-height: 1.2;
|
|
308
|
+
padding: 0.1rem 0.4rem;
|
|
309
|
+
text-transform: uppercase;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.ui-severity[data-level='critical'] {
|
|
313
|
+
background: var(--danger);
|
|
314
|
+
color: var(--button-text);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/* A tint over an unknown host surface cannot be contrast-gated, so the neutral
|
|
318
|
+
tier keeps a border instead and stays legible anywhere. */
|
|
319
|
+
.ui-severity[data-level='unknown'] {
|
|
320
|
+
background: none;
|
|
321
|
+
border: 1px solid var(--line-strong);
|
|
322
|
+
padding-block: calc(0.1rem - 1px);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/* The dot — for dense rows where a chip costs more width than the level is
|
|
326
|
+
worth. An accelerator for scanning, not the label. */
|
|
327
|
+
.ui-severity-dot {
|
|
328
|
+
background: var(--severity-tone);
|
|
329
|
+
border-radius: 50%;
|
|
330
|
+
block-size: 0.5rem;
|
|
331
|
+
display: inline-block;
|
|
332
|
+
flex: none;
|
|
333
|
+
inline-size: 0.5rem;
|
|
334
|
+
print-color-adjust: exact;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.ui-severity-dot[data-level='unknown'] {
|
|
338
|
+
background: none;
|
|
339
|
+
box-shadow: inset 0 0 0 1px var(--line-strong);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/* The row — a list item that carries a level. The inline-start gutter is what
|
|
343
|
+
makes a long list readable without reading it. */
|
|
344
|
+
.ui-severity-row {
|
|
345
|
+
align-items: baseline;
|
|
346
|
+
border-block-end: 1px solid var(--line);
|
|
347
|
+
border-inline-start: 2px solid var(--severity-tone);
|
|
348
|
+
display: flex;
|
|
349
|
+
gap: var(--space-xs);
|
|
350
|
+
padding: var(--space-2xs) var(--space-xs);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.ui-severity-row:last-child {
|
|
354
|
+
border-block-end: 0;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/* Gives up space first, so the level and any trailing metadata stay put. */
|
|
358
|
+
.ui-severity-row__title {
|
|
359
|
+
flex: 1 1 auto;
|
|
360
|
+
min-inline-size: 0;
|
|
361
|
+
overflow: hidden;
|
|
362
|
+
text-overflow: ellipsis;
|
|
363
|
+
white-space: nowrap;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
.ui-severity-row__meta {
|
|
367
|
+
color: var(--text-dim);
|
|
368
|
+
flex: none;
|
|
369
|
+
font-family: var(--mono);
|
|
370
|
+
font-size: var(--text-2xs);
|
|
371
|
+
}
|
|
372
|
+
|
|
226
373
|
/* Forced colours: the tone dot collapses to a system colour, so the
|
|
227
374
|
author-written label remains the state channel (it already must be). */
|
|
228
375
|
@media (forced-colors: active) {
|
|
@@ -237,4 +384,18 @@
|
|
|
237
384
|
.ui-job__bar {
|
|
238
385
|
background: CanvasText;
|
|
239
386
|
}
|
|
387
|
+
|
|
388
|
+
/* Same contract for severity: the tint and the gutter both vanish under a
|
|
389
|
+
replaced palette, so keep the structure and let the label carry meaning. */
|
|
390
|
+
.ui-severity {
|
|
391
|
+
border: 1px solid currentColor;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.ui-severity-row {
|
|
395
|
+
border-inline-start-color: CanvasText;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
.ui-severity-dot {
|
|
399
|
+
background: CanvasText;
|
|
400
|
+
}
|
|
240
401
|
}
|
package/css/table.css
CHANGED
package/css/tokens.css
CHANGED
|
@@ -25,6 +25,12 @@
|
|
|
25
25
|
--space-lg: 1.35rem;
|
|
26
26
|
--space-xl: 1.75rem;
|
|
27
27
|
--space-2xl: 2.5rem;
|
|
28
|
+
--tap-target: max(44px, 2.9rem);
|
|
29
|
+
--tap-target-min: max(24px, 1.6rem);
|
|
30
|
+
--safe-area-top: env(safe-area-inset-top, 0px);
|
|
31
|
+
--safe-area-right: env(safe-area-inset-right, 0px);
|
|
32
|
+
--safe-area-bottom: env(safe-area-inset-bottom, 0px);
|
|
33
|
+
--safe-area-left: env(safe-area-inset-left, 0px);
|
|
28
34
|
|
|
29
35
|
/* Type — Doto is the dot-matrix display face; body stays mono-grotesque. */
|
|
30
36
|
--mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', ui-monospace, monospace;
|
package/css/workbench.css
CHANGED
|
@@ -56,6 +56,36 @@
|
|
|
56
56
|
padding: 0.25rem;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/* Pane-scale — a control bar belonging to ONE pane rather than to the app. The
|
|
60
|
+
difference from `--compact` is not density but framing: this bar is a row
|
|
61
|
+
inside a surface that already has a border, so it drops its own frame and
|
|
62
|
+
corners and rules off from the content below instead. It also refuses to
|
|
63
|
+
wrap: a pane bar sits directly above content that may be a live terminal or
|
|
64
|
+
an editor, and a second row would resize that content on every state change.
|
|
65
|
+
The row scrolls instead — see `__fill` for what gives up the space. */
|
|
66
|
+
.ui-toolstrip--pane {
|
|
67
|
+
background: none;
|
|
68
|
+
border: 0;
|
|
69
|
+
border-block-end: 1px solid var(--line);
|
|
70
|
+
border-radius: 0;
|
|
71
|
+
flex-wrap: nowrap;
|
|
72
|
+
gap: var(--space-2xs);
|
|
73
|
+
overflow-x: auto;
|
|
74
|
+
padding: 0.3rem var(--space-xs);
|
|
75
|
+
scrollbar-width: thin;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* The element that absorbs slack and gives it back first — a title, a path, a
|
|
79
|
+
filter input. Everything else in the bar keeps its size, so the control that
|
|
80
|
+
would otherwise be pushed past the clipped edge stays reachable. */
|
|
81
|
+
.ui-toolstrip__fill {
|
|
82
|
+
flex: 1 1 auto;
|
|
83
|
+
min-inline-size: 0;
|
|
84
|
+
overflow: hidden;
|
|
85
|
+
text-overflow: ellipsis;
|
|
86
|
+
white-space: nowrap;
|
|
87
|
+
}
|
|
88
|
+
|
|
59
89
|
.ui-toolstrip__brand {
|
|
60
90
|
align-items: baseline;
|
|
61
91
|
display: inline-flex;
|
|
@@ -168,6 +198,96 @@
|
|
|
168
198
|
min-inline-size: 0;
|
|
169
199
|
}
|
|
170
200
|
|
|
201
|
+
/* --- Pane — a movable, renameable surface with a grab header.
|
|
202
|
+
|
|
203
|
+
`.ui-panel` is a padded card and `.ui-inspector` is head-plus-body; neither
|
|
204
|
+
is a window. A pane is what a canvas node, a floating tool window, or a
|
|
205
|
+
dockable panel needs: a header you can drag, a title that renames in place,
|
|
206
|
+
an actions slot that survives a narrow pane, and a body that owns the rest.
|
|
207
|
+
|
|
208
|
+
Bronto paints the frame only. The host owns dragging, z-order, focus policy,
|
|
209
|
+
persistence, and what the actions do — this leaf has no behavior. --- */
|
|
210
|
+
.ui-pane {
|
|
211
|
+
background: var(--panel);
|
|
212
|
+
border: 1px solid var(--line);
|
|
213
|
+
border-radius: var(--radius-md);
|
|
214
|
+
display: flex;
|
|
215
|
+
flex-direction: column;
|
|
216
|
+
min-block-size: 0;
|
|
217
|
+
min-inline-size: 0;
|
|
218
|
+
overflow: hidden;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* `cursor: grab` states the affordance; the host wires the gesture. */
|
|
222
|
+
.ui-pane__head {
|
|
223
|
+
align-items: center;
|
|
224
|
+
background: var(--bg-elevated);
|
|
225
|
+
border-block-end: 1px solid var(--line);
|
|
226
|
+
cursor: grab;
|
|
227
|
+
display: flex;
|
|
228
|
+
flex: none;
|
|
229
|
+
gap: var(--space-2xs);
|
|
230
|
+
min-block-size: var(--pane-head, 2.5rem);
|
|
231
|
+
padding: var(--space-2xs) var(--space-xs);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.ui-pane__head:active {
|
|
235
|
+
cursor: grabbing;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/* The title gives up space FIRST — it is the one thing in the header that can
|
|
239
|
+
be truncated without losing a function. */
|
|
240
|
+
.ui-pane__title {
|
|
241
|
+
flex: 1 1 auto;
|
|
242
|
+
font-size: var(--text-xs);
|
|
243
|
+
min-inline-size: 0;
|
|
244
|
+
overflow: hidden;
|
|
245
|
+
text-overflow: ellipsis;
|
|
246
|
+
white-space: nowrap;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/* Rename in place. It inherits the type it replaces so the swap from label to
|
|
250
|
+
input does not move a single pixel of layout; only the accent border says
|
|
251
|
+
"you are typing a name now". Pair with `.ui-pane__title` on the input too. */
|
|
252
|
+
.ui-pane__title-input {
|
|
253
|
+
background: var(--bg-elevated);
|
|
254
|
+
border: 1px solid var(--accent);
|
|
255
|
+
border-radius: var(--radius-sm);
|
|
256
|
+
color: inherit;
|
|
257
|
+
font: inherit;
|
|
258
|
+
letter-spacing: inherit;
|
|
259
|
+
margin-block: -1px;
|
|
260
|
+
max-inline-size: 100%;
|
|
261
|
+
min-inline-size: 0;
|
|
262
|
+
padding: 0 0.25rem;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/* Shrinks only AFTER the title is gone, and then scrolls rather than pushing
|
|
266
|
+
its last control past the pane's clipped edge — the failure that hides a
|
|
267
|
+
control the user needs (close, focus, disconnect) with no way to reach it. */
|
|
268
|
+
.ui-pane__actions {
|
|
269
|
+
display: flex;
|
|
270
|
+
flex: 0 1 auto;
|
|
271
|
+
flex-wrap: nowrap;
|
|
272
|
+
gap: var(--space-2xs);
|
|
273
|
+
min-inline-size: 0;
|
|
274
|
+
overflow-x: auto;
|
|
275
|
+
scrollbar-width: thin;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.ui-pane__actions:empty {
|
|
279
|
+
display: none;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
.ui-pane__body {
|
|
283
|
+
display: flex;
|
|
284
|
+
flex: 1;
|
|
285
|
+
flex-direction: column;
|
|
286
|
+
min-block-size: 0;
|
|
287
|
+
min-inline-size: 0;
|
|
288
|
+
overflow: auto;
|
|
289
|
+
}
|
|
290
|
+
|
|
171
291
|
/* --- Selection bar — actions on the current selection ("3 selected"). --- */
|
|
172
292
|
.ui-selectionbar {
|
|
173
293
|
align-items: center;
|
|
@@ -194,6 +314,37 @@
|
|
|
194
314
|
gap: var(--space-xs);
|
|
195
315
|
}
|
|
196
316
|
|
|
317
|
+
/* Anchor a floating bar to a viewport edge, clear of any display cutout or
|
|
318
|
+
gesture area. Both `--floating` bars above are shadowed but position-less,
|
|
319
|
+
so every consumer re-derives this — including the `max(offset, inset)` shape,
|
|
320
|
+
which is the part people get wrong by writing a bare offset that a phone then
|
|
321
|
+
swallows. `--anchor-block-end` is the default because a bar the thumb must
|
|
322
|
+
reach belongs at the bottom; `--anchor-block-start` is for the bar that must
|
|
323
|
+
not be under the thumb (a destructive-action or recovery bar).
|
|
324
|
+
|
|
325
|
+
The host still owns z-index — it knows what else is on the canvas. */
|
|
326
|
+
.ui-selectionbar--anchored,
|
|
327
|
+
.ui-toolstrip--anchored {
|
|
328
|
+
--anchor-offset: var(--space-sm);
|
|
329
|
+
|
|
330
|
+
inset-inline-start: 50%;
|
|
331
|
+
max-inline-size: calc(100% - 2 * var(--space-md));
|
|
332
|
+
position: absolute;
|
|
333
|
+
transform: translateX(-50%);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
.ui-selectionbar--anchored,
|
|
337
|
+
.ui-toolstrip--anchored,
|
|
338
|
+
.ui-selectionbar--anchor-block-end,
|
|
339
|
+
.ui-toolstrip--anchor-block-end {
|
|
340
|
+
inset-block: auto max(var(--anchor-offset), var(--safe-area-bottom));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
.ui-selectionbar--anchor-block-start,
|
|
344
|
+
.ui-toolstrip--anchor-block-start {
|
|
345
|
+
inset-block: max(var(--anchor-offset), var(--safe-area-top)) auto;
|
|
346
|
+
}
|
|
347
|
+
|
|
197
348
|
@media (hover: hover) {
|
|
198
349
|
.ui-segmented-buttons__button:hover:not(:disabled, [aria-disabled='true'], [aria-pressed='true'], .is-active) {
|
|
199
350
|
background: var(--panel);
|