@ponchia/ui 0.3.6 → 0.4.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 +1063 -0
- package/README.md +31 -14
- package/behaviors/index.d.ts +3 -1
- package/behaviors/index.js +179 -106
- package/classes/index.d.ts +41 -0
- package/classes/index.js +42 -0
- package/classes/vscode.css-custom-data.json +18 -22
- package/css/dataviz.css +96 -0
- package/css/disclosure.css +29 -0
- package/css/dots.css +32 -2
- package/css/feedback.css +53 -0
- package/css/motion.css +88 -0
- package/css/overlay.css +52 -1
- package/css/report.css +382 -0
- package/css/skins.css +54 -0
- package/css/tokens.css +56 -32
- package/dist/bronto.css +1 -1
- package/dist/css/dataviz.css +1 -0
- package/dist/css/disclosure.css +1 -1
- package/dist/css/dots.css +1 -1
- package/dist/css/feedback.css +1 -1
- package/dist/css/motion.css +1 -1
- package/dist/css/overlay.css +1 -1
- package/dist/css/report.css +1 -0
- package/dist/css/skins.css +1 -0
- package/dist/css/tokens.css +1 -1
- package/docs/adr/0001-color-system.md +271 -0
- package/docs/adr/0002-scope-and-2026-baseline.md +104 -0
- package/docs/adr/0003-theme-model.md +94 -0
- package/docs/contrast.md +170 -51
- package/docs/reference.md +118 -19
- package/docs/reporting.md +270 -0
- package/docs/stability.md +37 -0
- package/docs/theming.md +84 -7
- package/docs/usage.md +67 -8
- package/glyphs/glyphs.d.ts +14 -1
- package/glyphs/glyphs.js +143 -2
- package/llms.txt +101 -5
- package/package.json +79 -5
- package/qwik/index.d.ts +55 -0
- package/qwik/index.js +129 -0
- package/react/index.d.ts +57 -0
- package/react/index.js +84 -0
- package/solid/index.d.ts +57 -0
- package/solid/index.js +85 -0
- package/tokens/charts.d.ts +37 -0
- package/tokens/charts.js +96 -0
- package/tokens/charts.json +61 -0
- package/tokens/index.d.ts +3 -3
- package/tokens/index.js +16 -18
- package/tokens/index.json +32 -36
- package/tokens/resolved.json +39 -41
- package/tokens/skins.d.ts +27 -0
- package/tokens/skins.js +62 -0
- package/tokens/tokens.dtcg.json +22 -34
package/classes/index.js
CHANGED
|
@@ -70,6 +70,7 @@ export const cls = Object.freeze({
|
|
|
70
70
|
dotmatrixCellAccent: 'ui-dotmatrix__cell--accent',
|
|
71
71
|
dotmatrixReveal: 'ui-dotmatrix--reveal',
|
|
72
72
|
dotmatrixPulse: 'ui-dotmatrix--pulse',
|
|
73
|
+
icon: 'ui-icon',
|
|
73
74
|
dotfield: 'ui-dotfield',
|
|
74
75
|
dotrule: 'ui-dotrule',
|
|
75
76
|
dotbar: 'ui-dotbar',
|
|
@@ -244,6 +245,44 @@ export const cls = Object.freeze({
|
|
|
244
245
|
timeline: 'ui-timeline',
|
|
245
246
|
timelineItem: 'ui-timeline__item',
|
|
246
247
|
timelineTime: 'ui-timeline__time',
|
|
248
|
+
// report kit (opt-in css/report.css)
|
|
249
|
+
report: 'ui-report',
|
|
250
|
+
reportCompact: 'ui-report--compact',
|
|
251
|
+
reportNumbered: 'ui-report--numbered',
|
|
252
|
+
reportCover: 'ui-report__cover',
|
|
253
|
+
reportCoverCompact: 'ui-report__cover--compact',
|
|
254
|
+
reportHeader: 'ui-report__header',
|
|
255
|
+
reportTitle: 'ui-report__title',
|
|
256
|
+
reportSubtitle: 'ui-report__subtitle',
|
|
257
|
+
reportMeta: 'ui-report__meta',
|
|
258
|
+
reportToc: 'ui-report__toc',
|
|
259
|
+
reportSummary: 'ui-report__summary',
|
|
260
|
+
reportSection: 'ui-report__section',
|
|
261
|
+
reportSectionUnnumbered: 'ui-report__section--unnumbered',
|
|
262
|
+
reportSectionHead: 'ui-report__section-head',
|
|
263
|
+
reportFinding: 'ui-report__finding',
|
|
264
|
+
reportEvidence: 'ui-report__evidence',
|
|
265
|
+
reportFigure: 'ui-report__figure',
|
|
266
|
+
reportCaption: 'ui-report__caption',
|
|
267
|
+
reportSources: 'ui-report__sources',
|
|
268
|
+
reportAppendix: 'ui-report__appendix',
|
|
269
|
+
reportFootnotes: 'ui-report__footnotes',
|
|
270
|
+
chart: 'ui-chart',
|
|
271
|
+
chartLegend: 'ui-chart__legend',
|
|
272
|
+
chartSwatch: 'ui-chart__swatch',
|
|
273
|
+
chartCaption: 'ui-chart__caption',
|
|
274
|
+
chartPlot: 'ui-chart__plot',
|
|
275
|
+
chartBar: 'ui-chart__bar',
|
|
276
|
+
chartLabel: 'ui-chart__label',
|
|
277
|
+
chartTrack: 'ui-chart__track',
|
|
278
|
+
chartFill: 'ui-chart__fill',
|
|
279
|
+
chartFallback: 'ui-chart__fallback',
|
|
280
|
+
printOnly: 'ui-print-only',
|
|
281
|
+
screenOnly: 'ui-screen-only',
|
|
282
|
+
breakBefore: 'ui-break-before',
|
|
283
|
+
breakAfter: 'ui-break-after',
|
|
284
|
+
keep: 'ui-keep',
|
|
285
|
+
printExact: 'ui-print-exact',
|
|
247
286
|
kbd: 'ui-kbd',
|
|
248
287
|
display: 'ui-display',
|
|
249
288
|
mono: 'ui-mono',
|
|
@@ -261,6 +300,9 @@ export const cls = Object.freeze({
|
|
|
261
300
|
animateFade: 'ui-animate-fade',
|
|
262
301
|
animateDot: 'ui-animate-dot',
|
|
263
302
|
animateMatrix: 'ui-animate-matrix',
|
|
303
|
+
scrollProgress: 'ui-scroll-progress',
|
|
304
|
+
scrollReveal: 'ui-scroll-reveal',
|
|
305
|
+
vt: 'ui-vt',
|
|
264
306
|
// admin shell (was the legacy .app-* vocabulary; promoted in 0.3.0)
|
|
265
307
|
appShell: 'ui-app-shell',
|
|
266
308
|
appShellFull: 'ui-app-shell--full',
|
|
@@ -7,19 +7,19 @@
|
|
|
7
7
|
},
|
|
8
8
|
{
|
|
9
9
|
"name": "--accent-1",
|
|
10
|
-
"description": "Global scale token. Value: `color-mix(in
|
|
10
|
+
"description": "Global scale token. Value: `color-mix(in oklch, var(--accent) 8%, var(--accent-ramp-end))`"
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
"name": "--accent-2",
|
|
14
|
-
"description": "Global scale token. Value: `color-mix(in
|
|
14
|
+
"description": "Global scale token. Value: `color-mix(in oklch, var(--accent) 16%, var(--accent-ramp-end))`"
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
"name": "--accent-3",
|
|
18
|
-
"description": "Global scale token. Value: `color-mix(in
|
|
18
|
+
"description": "Global scale token. Value: `color-mix(in oklch, var(--accent) 32%, var(--accent-ramp-end))`"
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
"name": "--accent-4",
|
|
22
|
-
"description": "Global scale token. Value: `color-mix(in
|
|
22
|
+
"description": "Global scale token. Value: `color-mix(in oklch, var(--accent) 60%, var(--accent-ramp-end))`"
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
"name": "--accent-5",
|
|
@@ -29,6 +29,10 @@
|
|
|
29
29
|
"name": "--accent-6",
|
|
30
30
|
"description": "Global scale token. Value: `var(--accent-strong)`"
|
|
31
31
|
},
|
|
32
|
+
{
|
|
33
|
+
"name": "--accent-ramp-end",
|
|
34
|
+
"description": "Theme token. Light: `#ffffff` · Dark: `#000000`"
|
|
35
|
+
},
|
|
32
36
|
{
|
|
33
37
|
"name": "--accent-soft",
|
|
34
38
|
"description": "Theme token. Light: `color-mix(in srgb, var(--accent) 10%, transparent)` · Dark: `color-mix(in srgb, var(--accent) 14%, transparent)`"
|
|
@@ -43,7 +47,7 @@
|
|
|
43
47
|
},
|
|
44
48
|
{
|
|
45
49
|
"name": "--bg",
|
|
46
|
-
"description": "Theme token. Light: `#f4f4f2` · Dark: `#
|
|
50
|
+
"description": "Theme token. Light: `#f4f4f2` · Dark: `#121212`"
|
|
47
51
|
},
|
|
48
52
|
{
|
|
49
53
|
"name": "--bg-accent",
|
|
@@ -51,7 +55,7 @@
|
|
|
51
55
|
},
|
|
52
56
|
{
|
|
53
57
|
"name": "--bg-elevated",
|
|
54
|
-
"description": "Theme token. Light: `#fbfbfa` · Dark: `#
|
|
58
|
+
"description": "Theme token. Light: `#fbfbfa` · Dark: `#181818`"
|
|
55
59
|
},
|
|
56
60
|
{
|
|
57
61
|
"name": "--border",
|
|
@@ -199,35 +203,27 @@
|
|
|
199
203
|
},
|
|
200
204
|
{
|
|
201
205
|
"name": "--line",
|
|
202
|
-
"description": "Theme token. Light: `#d8d8d4` · Dark: `#
|
|
206
|
+
"description": "Theme token. Light: `#d8d8d4` · Dark: `#383838`"
|
|
203
207
|
},
|
|
204
208
|
{
|
|
205
209
|
"name": "--line-strong",
|
|
206
|
-
"description": "Theme token. Light: `#a8a8a2` · Dark: `#
|
|
210
|
+
"description": "Theme token. Light: `#a8a8a2` · Dark: `#555555`"
|
|
207
211
|
},
|
|
208
212
|
{
|
|
209
213
|
"name": "--mono",
|
|
210
214
|
"description": "Global scale token. Value: `'JetBrains Mono', 'Fira Code', 'Cascadia Code', 'SF Mono', ui-monospace, monospace`"
|
|
211
215
|
},
|
|
212
|
-
{
|
|
213
|
-
"name": "--orange",
|
|
214
|
-
"description": "Theme token. Light: `#a85f32` · Dark: `#d08c5b`"
|
|
215
|
-
},
|
|
216
|
-
{
|
|
217
|
-
"name": "--orange-soft",
|
|
218
|
-
"description": "Theme token. Light: `rgb(168, 95, 50, 0.13)` · Dark: `rgb(208, 140, 91, 0.15)`"
|
|
219
|
-
},
|
|
220
216
|
{
|
|
221
217
|
"name": "--panel",
|
|
222
|
-
"description": "Theme token. Light: `#ffffff` · Dark: `#
|
|
218
|
+
"description": "Theme token. Light: `#ffffff` · Dark: `#1c1c1c`"
|
|
223
219
|
},
|
|
224
220
|
{
|
|
225
221
|
"name": "--panel-soft",
|
|
226
|
-
"description": "Theme token. Light: `#ececea` · Dark: `#
|
|
222
|
+
"description": "Theme token. Light: `#ececea` · Dark: `#242424`"
|
|
227
223
|
},
|
|
228
224
|
{
|
|
229
225
|
"name": "--panel-strong",
|
|
230
|
-
"description": "Theme token. Light: `#ffffff` · Dark: `#
|
|
226
|
+
"description": "Theme token. Light: `#ffffff` · Dark: `#222222`"
|
|
231
227
|
},
|
|
232
228
|
{
|
|
233
229
|
"name": "--radius-lg",
|
|
@@ -335,7 +331,7 @@
|
|
|
335
331
|
},
|
|
336
332
|
{
|
|
337
333
|
"name": "--text",
|
|
338
|
-
"description": "Theme token. Light: `#0a0a0a` · Dark: `#
|
|
334
|
+
"description": "Theme token. Light: `#0a0a0a` · Dark: `#e6e6e6`"
|
|
339
335
|
},
|
|
340
336
|
{
|
|
341
337
|
"name": "--text-2xs",
|
|
@@ -347,7 +343,7 @@
|
|
|
347
343
|
},
|
|
348
344
|
{
|
|
349
345
|
"name": "--text-dim",
|
|
350
|
-
"description": "Theme token. Light: `#686863` · Dark: `#
|
|
346
|
+
"description": "Theme token. Light: `#686863` · Dark: `#a0a0a0`"
|
|
351
347
|
},
|
|
352
348
|
{
|
|
353
349
|
"name": "--text-lg",
|
|
@@ -359,7 +355,7 @@
|
|
|
359
355
|
},
|
|
360
356
|
{
|
|
361
357
|
"name": "--text-soft",
|
|
362
|
-
"description": "Theme token. Light: `#353533` · Dark: `#
|
|
358
|
+
"description": "Theme token. Light: `#353533` · Dark: `#c8c8c8`"
|
|
363
359
|
},
|
|
364
360
|
{
|
|
365
361
|
"name": "--text-xl",
|
package/css/dataviz.css
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/* @ponchia/ui — GENERATED from tokens/charts.js by scripts/gen-charts.mjs.
|
|
2
|
+
* Do not edit by hand; run `npm run charts:build`. Drift-checked in CI.
|
|
3
|
+
*
|
|
4
|
+
* Tier-4 data-viz palette (ADR-0001). OPT-IN: import `@ponchia/ui/css/dataviz.css`
|
|
5
|
+
* on demand; never in the default bundle, never UI chrome. --chart-1 is the live
|
|
6
|
+
* accent (series 1 = brand). Pair colour N with pattern N (a 2nd, non-colour
|
|
7
|
+
* channel): background: var(--chart-3); background-image: var(--chart-pattern-3);
|
|
8
|
+
* background-size: var(--chart-pattern-size); --chart-pattern-ink: <contrast>. */
|
|
9
|
+
|
|
10
|
+
:root {
|
|
11
|
+
--chart-1: var(--accent);
|
|
12
|
+
--chart-2: #e69f00;
|
|
13
|
+
--chart-3: #56b4e9;
|
|
14
|
+
--chart-4: #009e73;
|
|
15
|
+
--chart-5: #f0e442;
|
|
16
|
+
--chart-6: #0072b2;
|
|
17
|
+
--chart-7: #cc79a7;
|
|
18
|
+
--chart-8: #4d5358;
|
|
19
|
+
--chart-seq-1: oklch(94% 0.03 25deg);
|
|
20
|
+
--chart-seq-2: oklch(85% 0.07 25deg);
|
|
21
|
+
--chart-seq-3: oklch(74% 0.12 25deg);
|
|
22
|
+
--chart-seq-4: oklch(62% 0.16 25deg);
|
|
23
|
+
--chart-seq-5: oklch(50% 0.16 25deg);
|
|
24
|
+
--chart-seq-6: oklch(38% 0.13 25deg);
|
|
25
|
+
--chart-div-1: oklch(45% 0.14 255deg);
|
|
26
|
+
--chart-div-2: oklch(62% 0.1 250deg);
|
|
27
|
+
--chart-div-3: oklch(82% 0.05 245deg);
|
|
28
|
+
--chart-div-4: oklch(90% 0.01 250deg);
|
|
29
|
+
--chart-div-5: oklch(80% 0.07 60deg);
|
|
30
|
+
--chart-div-6: oklch(66% 0.13 55deg);
|
|
31
|
+
--chart-div-7: oklch(56% 0.15 45deg);
|
|
32
|
+
|
|
33
|
+
/* Dot-matrix pattern fills — pair with the matching colour (WCAG 1.4.1). */
|
|
34
|
+
--chart-pattern-size: 8px;
|
|
35
|
+
--chart-pattern-ink: rgb(0 0 0 / 0.34);
|
|
36
|
+
--chart-pattern-1: none;
|
|
37
|
+
--chart-pattern-2: radial-gradient(circle at 50% 50%, var(--chart-pattern-ink) 1.4px, transparent 1.6px);
|
|
38
|
+
--chart-pattern-3: radial-gradient(circle at 0 0, var(--chart-pattern-ink) 1.4px, transparent 1.6px);
|
|
39
|
+
--chart-pattern-4: radial-gradient(circle at 25% 25%, var(--chart-pattern-ink) 1.2px, transparent 1.4px), radial-gradient(circle at 75% 75%, var(--chart-pattern-ink) 1.2px, transparent 1.4px);
|
|
40
|
+
--chart-pattern-5: radial-gradient(circle at 50% 25%, var(--chart-pattern-ink) 1.2px, transparent 1.4px), radial-gradient(circle at 50% 75%, var(--chart-pattern-ink) 1.2px, transparent 1.4px);
|
|
41
|
+
--chart-pattern-6: radial-gradient(circle at 25% 50%, var(--chart-pattern-ink) 1.2px, transparent 1.4px), radial-gradient(circle at 75% 50%, var(--chart-pattern-ink) 1.2px, transparent 1.4px);
|
|
42
|
+
--chart-pattern-7: radial-gradient(circle at 25% 25%, var(--chart-pattern-ink) 1px, transparent 1.2px), radial-gradient(circle at 75% 25%, var(--chart-pattern-ink) 1px, transparent 1.2px), radial-gradient(circle at 25% 75%, var(--chart-pattern-ink) 1px, transparent 1.2px), radial-gradient(circle at 75% 75%, var(--chart-pattern-ink) 1px, transparent 1.2px);
|
|
43
|
+
--chart-pattern-8: radial-gradient(circle at 50% 50%, transparent 1.2px, var(--chart-pattern-ink) 1.4px, transparent 2.2px);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@media (prefers-color-scheme: dark) {
|
|
47
|
+
:root:not([data-theme='light']) {
|
|
48
|
+
--chart-1: var(--accent);
|
|
49
|
+
--chart-2: #e69f00;
|
|
50
|
+
--chart-3: #56b4e9;
|
|
51
|
+
--chart-4: #009e73;
|
|
52
|
+
--chart-5: #f0e442;
|
|
53
|
+
--chart-6: #0072b2;
|
|
54
|
+
--chart-7: #cc79a7;
|
|
55
|
+
--chart-8: #4d5358;
|
|
56
|
+
--chart-seq-1: oklch(30% 0.1 25deg);
|
|
57
|
+
--chart-seq-2: oklch(42% 0.15 25deg);
|
|
58
|
+
--chart-seq-3: oklch(55% 0.17 25deg);
|
|
59
|
+
--chart-seq-4: oklch(68% 0.15 25deg);
|
|
60
|
+
--chart-seq-5: oklch(80% 0.1 25deg);
|
|
61
|
+
--chart-seq-6: oklch(90% 0.05 25deg);
|
|
62
|
+
--chart-div-1: oklch(70% 0.13 250deg);
|
|
63
|
+
--chart-div-2: oklch(60% 0.12 252deg);
|
|
64
|
+
--chart-div-3: oklch(48% 0.08 255deg);
|
|
65
|
+
--chart-div-4: oklch(40% 0.01 250deg);
|
|
66
|
+
--chart-div-5: oklch(58% 0.1 60deg);
|
|
67
|
+
--chart-div-6: oklch(72% 0.13 58deg);
|
|
68
|
+
--chart-div-7: oklch(80% 0.12 55deg);
|
|
69
|
+
--chart-pattern-ink: rgb(255 255 255 / 0.42);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
:root[data-theme='dark'] {
|
|
74
|
+
--chart-1: var(--accent);
|
|
75
|
+
--chart-2: #e69f00;
|
|
76
|
+
--chart-3: #56b4e9;
|
|
77
|
+
--chart-4: #009e73;
|
|
78
|
+
--chart-5: #f0e442;
|
|
79
|
+
--chart-6: #0072b2;
|
|
80
|
+
--chart-7: #cc79a7;
|
|
81
|
+
--chart-8: #4d5358;
|
|
82
|
+
--chart-seq-1: oklch(30% 0.1 25deg);
|
|
83
|
+
--chart-seq-2: oklch(42% 0.15 25deg);
|
|
84
|
+
--chart-seq-3: oklch(55% 0.17 25deg);
|
|
85
|
+
--chart-seq-4: oklch(68% 0.15 25deg);
|
|
86
|
+
--chart-seq-5: oklch(80% 0.1 25deg);
|
|
87
|
+
--chart-seq-6: oklch(90% 0.05 25deg);
|
|
88
|
+
--chart-div-1: oklch(70% 0.13 250deg);
|
|
89
|
+
--chart-div-2: oklch(60% 0.12 252deg);
|
|
90
|
+
--chart-div-3: oklch(48% 0.08 255deg);
|
|
91
|
+
--chart-div-4: oklch(40% 0.01 250deg);
|
|
92
|
+
--chart-div-5: oklch(58% 0.1 60deg);
|
|
93
|
+
--chart-div-6: oklch(72% 0.13 58deg);
|
|
94
|
+
--chart-div-7: oklch(80% 0.12 55deg);
|
|
95
|
+
--chart-pattern-ink: rgb(255 255 255 / 0.42);
|
|
96
|
+
}
|
package/css/disclosure.css
CHANGED
|
@@ -128,6 +128,35 @@
|
|
|
128
128
|
margin-block-start: 0;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
/* Auto-height open/close for the native <details> accordion: animate the
|
|
132
|
+
content box from 0 to its intrinsic height. `interpolate-size:
|
|
133
|
+
allow-keywords` makes `auto` interpolable; `::details-content` exposes
|
|
134
|
+
the otherwise-anonymous content box; `content-visibility ... allow-discrete`
|
|
135
|
+
keeps the content laid out (and the dot marker's own rotation in sync)
|
|
136
|
+
through the close. Strict progressive enhancement — gated on
|
|
137
|
+
`@supports selector(::details-content)` (Chrome 131+/Safari 18.4+; Firefox
|
|
138
|
+
not yet) so engines without it just snap open/closed exactly as before —
|
|
139
|
+
and on `prefers-reduced-motion: no-preference`. */
|
|
140
|
+
@supports selector(::details-content) {
|
|
141
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
142
|
+
.ui-accordion {
|
|
143
|
+
interpolate-size: allow-keywords;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.ui-accordion__item::details-content {
|
|
147
|
+
block-size: 0;
|
|
148
|
+
overflow: hidden;
|
|
149
|
+
transition:
|
|
150
|
+
block-size var(--duration-base) var(--ease-out),
|
|
151
|
+
content-visibility var(--duration-base) allow-discrete;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.ui-accordion__item[open]::details-content {
|
|
155
|
+
block-size: auto;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
131
160
|
/* --- Segmented control — CSS-only radio group --- */
|
|
132
161
|
|
|
133
162
|
.ui-segmented {
|
package/css/dots.css
CHANGED
|
@@ -51,7 +51,16 @@
|
|
|
51
51
|
--dotmatrix-dot to a fixed dot length (intrinsic sizing, e.g. for the
|
|
52
52
|
display glyphs in @ponchia/ui/glyphs) instead of stretching to fit;
|
|
53
53
|
set --dotmatrix-dot-radius: 0 (with --dotmatrix-gap: 0) to fuse the dots
|
|
54
|
-
into a crisp pixel glyph that stays legible at small/inline sizes.
|
|
54
|
+
into a crisp pixel glyph that stays legible at small/inline sizes.
|
|
55
|
+
|
|
56
|
+
Tier-3 "display expression" knobs (see docs/adr/0001-color-system.md) — the
|
|
57
|
+
on-brand substitute for decorative colour, since a dot-matrix display speaks
|
|
58
|
+
in brightness + time, not hue. All default to a no-op, so the default render
|
|
59
|
+
is unchanged; the optional colorways (css/skins.css) set them:
|
|
60
|
+
--dotmatrix-glow phosphor bloom around lit cells (default 0 = off)
|
|
61
|
+
--dotmatrix-pulse-min the floor the --pulse animation dips to (default 0.55)
|
|
62
|
+
--dotmatrix-reveal-step per-cell scan cadence for --reveal (default 3ms)
|
|
63
|
+
Density stays on --dotmatrix-cols / --dotmatrix-gap / --dotmatrix-dot. */
|
|
55
64
|
.ui-dotmatrix {
|
|
56
65
|
display: grid;
|
|
57
66
|
gap: var(--dotmatrix-gap, 0.5rem);
|
|
@@ -64,12 +73,16 @@
|
|
|
64
73
|
border-radius: var(--dotmatrix-dot-radius, 50%);
|
|
65
74
|
}
|
|
66
75
|
|
|
76
|
+
/* Lit cells optionally bloom — a phosphor/LED glow in the cell's own colour.
|
|
77
|
+
0 blur by default → no shadow painted, so display use is unaffected. */
|
|
67
78
|
.ui-dotmatrix__cell--hot {
|
|
68
79
|
background: var(--field-dot-hot);
|
|
80
|
+
box-shadow: 0 0 var(--dotmatrix-glow, 0) var(--field-dot-hot);
|
|
69
81
|
}
|
|
70
82
|
|
|
71
83
|
.ui-dotmatrix__cell--accent {
|
|
72
84
|
background: var(--field-dot-accent);
|
|
85
|
+
box-shadow: 0 0 var(--dotmatrix-glow, 0) var(--field-dot-accent);
|
|
73
86
|
}
|
|
74
87
|
|
|
75
88
|
/* Animation (opt-in, decorative): a dot-matrix that "plays". Meaning stays
|
|
@@ -99,11 +112,28 @@
|
|
|
99
112
|
|
|
100
113
|
@keyframes dotmatrixPulse {
|
|
101
114
|
50% {
|
|
102
|
-
opacity: 0.55;
|
|
115
|
+
opacity: var(--dotmatrix-pulse-min, 0.55);
|
|
103
116
|
transform: scale(0.94);
|
|
104
117
|
}
|
|
105
118
|
}
|
|
106
119
|
|
|
120
|
+
/* Inline icon wrapper — the one-node glyph render path (renderGlyph(name,
|
|
121
|
+
{ render: 'mask' })). A single element masked by the glyph bitmap, so it
|
|
122
|
+
scales with the text and inherits currentColor — for icon-at-scale (e.g.
|
|
123
|
+
one in every table row) where the 256-cell .ui-dotmatrix is too heavy.
|
|
124
|
+
--icon-size (default 1em) sizes it; --icon-mask carries the bitmap. */
|
|
125
|
+
.ui-icon {
|
|
126
|
+
display: inline-block;
|
|
127
|
+
flex: 0 0 auto;
|
|
128
|
+
inline-size: var(--icon-size, 1em);
|
|
129
|
+
block-size: var(--icon-size, 1em);
|
|
130
|
+
background: currentcolor;
|
|
131
|
+
vertical-align: -0.125em;
|
|
132
|
+
/* stylelint-disable-next-line property-no-vendor-prefix -- Safari still needs the prefixed mask property. */
|
|
133
|
+
-webkit-mask: var(--icon-mask) center / contain no-repeat;
|
|
134
|
+
mask: var(--icon-mask) center / contain no-repeat;
|
|
135
|
+
}
|
|
136
|
+
|
|
107
137
|
/* Status dot — the glyph-style state indicator. */
|
|
108
138
|
.ui-dot {
|
|
109
139
|
background: var(--text-dim);
|
package/css/feedback.css
CHANGED
|
@@ -135,6 +135,14 @@
|
|
|
135
135
|
.ui-toast {
|
|
136
136
|
align-items: flex-start;
|
|
137
137
|
animation: uiToastIn var(--duration-base) var(--ease-spring) both;
|
|
138
|
+
|
|
139
|
+
/* Exit is a transition (not a keyframe): the behavior adds `.is-leaving`
|
|
140
|
+
and removes the node when this transition ends. Entrance stays the
|
|
141
|
+
keyframe above — the transition only fires on the `.is-leaving`
|
|
142
|
+
property change, never on insert. */
|
|
143
|
+
transition:
|
|
144
|
+
opacity var(--duration-base) var(--ease-out),
|
|
145
|
+
transform var(--duration-base) var(--ease-out);
|
|
138
146
|
background: var(--panel-strong);
|
|
139
147
|
border: 1px solid var(--line-strong);
|
|
140
148
|
border-radius: var(--radius-md);
|
|
@@ -149,6 +157,15 @@
|
|
|
149
157
|
inline-size: 100%;
|
|
150
158
|
}
|
|
151
159
|
|
|
160
|
+
/* Leaving — fade + drop out. `animation: none` releases the entrance
|
|
161
|
+
keyframe's `both` fill (a filling animation outranks normal property
|
|
162
|
+
declarations, so without this the exit values would be ignored). */
|
|
163
|
+
.ui-toast.is-leaving {
|
|
164
|
+
animation: none;
|
|
165
|
+
opacity: 0;
|
|
166
|
+
transform: translateY(6px) scale(0.98);
|
|
167
|
+
}
|
|
168
|
+
|
|
152
169
|
.ui-toast::before {
|
|
153
170
|
background: var(--text-dim);
|
|
154
171
|
border-radius: 50%;
|
|
@@ -302,6 +319,41 @@
|
|
|
302
319
|
display: none;
|
|
303
320
|
}
|
|
304
321
|
|
|
322
|
+
/* Enter AND exit motion — zero JS, the same `@starting-style` +
|
|
323
|
+
`transition-behavior: allow-discrete` recipe as the modal (overlay.css).
|
|
324
|
+
It covers BOTH paths initPopover can take: the native `[popover]` top
|
|
325
|
+
layer (where the `overlay` transition keeps the panel painted while it
|
|
326
|
+
animates out before leaving the layer) and the `.is-open` fallback (a
|
|
327
|
+
regular element, so `display allow-discrete` alone carries the exit).
|
|
328
|
+
Gated on `prefers-reduced-motion: no-preference`, so reduced-motion users
|
|
329
|
+
keep the instant open/close they had before. JS positions the panel via
|
|
330
|
+
`top`/`left`, so the entrance `transform` is free to animate. */
|
|
331
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
332
|
+
.ui-popover {
|
|
333
|
+
opacity: 0;
|
|
334
|
+
transform: translateY(-4px) scale(0.98);
|
|
335
|
+
transition:
|
|
336
|
+
opacity var(--duration-fast) var(--ease-out),
|
|
337
|
+
transform var(--duration-fast) var(--ease-out),
|
|
338
|
+
overlay var(--duration-fast) allow-discrete,
|
|
339
|
+
display var(--duration-fast) allow-discrete;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
.ui-popover.is-open,
|
|
343
|
+
.ui-popover[popover]:popover-open {
|
|
344
|
+
opacity: 1;
|
|
345
|
+
transform: none;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
@starting-style {
|
|
349
|
+
.ui-popover.is-open,
|
|
350
|
+
.ui-popover[popover]:popover-open {
|
|
351
|
+
opacity: 0;
|
|
352
|
+
transform: translateY(-4px) scale(0.98);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
305
357
|
/* --- Progress — linear, determinate or indeterminate --- */
|
|
306
358
|
|
|
307
359
|
.ui-progress {
|
|
@@ -336,6 +388,7 @@
|
|
|
336
388
|
|
|
337
389
|
.ui-toast {
|
|
338
390
|
animation: none;
|
|
391
|
+
transition: none;
|
|
339
392
|
}
|
|
340
393
|
}
|
|
341
394
|
|
package/css/motion.css
CHANGED
|
@@ -172,6 +172,15 @@
|
|
|
172
172
|
}
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
+
/* Scroll progress: a bar that fills as the page scrolls. Driven by a
|
|
176
|
+
scroll() timeline, not time — the base scaleX(0) is the static
|
|
177
|
+
(unsupported) end state. */
|
|
178
|
+
@keyframes uiScrollGrow {
|
|
179
|
+
to {
|
|
180
|
+
transform: scaleX(1);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
175
184
|
/* --- Utilities --- */
|
|
176
185
|
|
|
177
186
|
.ui-animate-in {
|
|
@@ -272,6 +281,85 @@
|
|
|
272
281
|
margin-inline-start: 0.1em;
|
|
273
282
|
}
|
|
274
283
|
|
|
284
|
+
/* --- Scroll-driven (progressive enhancement) — the scroll/view timeline
|
|
285
|
+
IS the engine, no JS. Everything is gated on `@supports
|
|
286
|
+
(animation-timeline: …)` so engines without it (today, Firefox/Safari)
|
|
287
|
+
keep the static end state, and on `prefers-reduced-motion: no-preference`
|
|
288
|
+
(a scroll timeline ignores animation-duration, so the global reduced-motion
|
|
289
|
+
reset below can't neutralise it — it must be gated here). --- */
|
|
290
|
+
|
|
291
|
+
/* Reading-progress bar. Fixed hairline that fills with document scroll;
|
|
292
|
+
unsupported → a static, empty (scaleX(0)) bar. Pair with role="progressbar"
|
|
293
|
+
if you surface it to AT. */
|
|
294
|
+
.ui-scroll-progress {
|
|
295
|
+
background: var(--accent);
|
|
296
|
+
block-size: 3px;
|
|
297
|
+
inset-block-start: 0;
|
|
298
|
+
inset-inline: 0;
|
|
299
|
+
position: fixed;
|
|
300
|
+
transform: scaleX(0);
|
|
301
|
+
transform-origin: 0 50%;
|
|
302
|
+
z-index: var(--z-overlay);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/* RTL: fill from the inline-start (right) edge. */
|
|
306
|
+
[dir='rtl'] .ui-scroll-progress {
|
|
307
|
+
transform-origin: 100% 50%;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
@supports (animation-timeline: scroll()) {
|
|
311
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
312
|
+
.ui-scroll-progress {
|
|
313
|
+
animation: uiScrollGrow linear;
|
|
314
|
+
|
|
315
|
+
/* longhand AFTER the shorthand — `animation:` resets the timeline to
|
|
316
|
+
auto, so this must follow it to take effect. */
|
|
317
|
+
animation-timeline: scroll(root block);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/* Reveal-on-scroll with no JS and no IntersectionObserver: the element
|
|
323
|
+
rises + fades as it scrolls into view. Unsupported → fully visible
|
|
324
|
+
(the `uiRise` end state), same graceful default as `.ui-reveal`. */
|
|
325
|
+
@supports (animation-timeline: view()) {
|
|
326
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
327
|
+
.ui-scroll-reveal {
|
|
328
|
+
animation: uiRise linear both;
|
|
329
|
+
animation-timeline: view();
|
|
330
|
+
animation-range: entry 0% cover 40%;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/* --- View Transitions (progressive enhancement) — `.ui-vt` names an
|
|
336
|
+
element so it morphs across a same-document `document.startViewTransition()`
|
|
337
|
+
or a cross-document navigation. Opt into the cross-document kind with a
|
|
338
|
+
top-level, *unlayered* `@view-transition { navigation: auto; }` in your own
|
|
339
|
+
CSS (it is document-global, so the framework can't ship or scope it). --- */
|
|
340
|
+
.ui-vt {
|
|
341
|
+
view-transition-name: var(--ui-vt-name, none);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
345
|
+
/* On-brand default for the root cross-fade, inert until a transition runs. */
|
|
346
|
+
::view-transition-old(root),
|
|
347
|
+
::view-transition-new(root) {
|
|
348
|
+
animation-duration: var(--duration-base);
|
|
349
|
+
animation-timing-function: var(--ease-standard);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
@media (prefers-reduced-motion: reduce) {
|
|
354
|
+
/* View transitions do NOT honour reduced-motion automatically and the
|
|
355
|
+
::view-transition tree is outside the `*` reset below — kill it here. */
|
|
356
|
+
::view-transition-group(*),
|
|
357
|
+
::view-transition-old(*),
|
|
358
|
+
::view-transition-new(*) {
|
|
359
|
+
animation: none !important;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
275
363
|
@media (prefers-reduced-motion: reduce) {
|
|
276
364
|
html {
|
|
277
365
|
scroll-behavior: auto;
|
package/css/overlay.css
CHANGED
|
@@ -26,11 +26,56 @@
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
.ui-modal[open] {
|
|
29
|
-
animation: uiToastIn var(--duration-base) var(--ease-spring) both;
|
|
30
29
|
display: grid;
|
|
31
30
|
grid-template-rows: auto 1fr auto;
|
|
32
31
|
}
|
|
33
32
|
|
|
33
|
+
/* Enter AND exit, zero JS — `@starting-style` + `transition-behavior:
|
|
34
|
+
allow-discrete` keep the native <dialog> (and its backdrop) in the top
|
|
35
|
+
layer while it transitions out on close, so it fades/scales both ways
|
|
36
|
+
instead of only animating in. Scoped to `dialog.ui-modal` so the
|
|
37
|
+
controlled `.is-open` (non-<dialog>) path keeps its own entrance.
|
|
38
|
+
Needs a 2026-era floor (see CONTRIBUTING → Browser floor). */
|
|
39
|
+
dialog.ui-modal {
|
|
40
|
+
opacity: 0;
|
|
41
|
+
transform: translateY(6px) scale(0.98);
|
|
42
|
+
transition:
|
|
43
|
+
opacity var(--duration-base) var(--ease-spring),
|
|
44
|
+
transform var(--duration-base) var(--ease-spring),
|
|
45
|
+
overlay var(--duration-base) allow-discrete,
|
|
46
|
+
display var(--duration-base) allow-discrete;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
dialog.ui-modal[open] {
|
|
50
|
+
opacity: 1;
|
|
51
|
+
transform: none;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@starting-style {
|
|
55
|
+
dialog.ui-modal[open] {
|
|
56
|
+
opacity: 0;
|
|
57
|
+
transform: translateY(6px) scale(0.98);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
dialog.ui-modal::backdrop {
|
|
62
|
+
opacity: 0;
|
|
63
|
+
transition:
|
|
64
|
+
opacity var(--duration-base) var(--ease-out),
|
|
65
|
+
overlay var(--duration-base) allow-discrete,
|
|
66
|
+
display var(--duration-base) allow-discrete;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
dialog.ui-modal[open]::backdrop {
|
|
70
|
+
opacity: 1;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@starting-style {
|
|
74
|
+
dialog.ui-modal[open]::backdrop {
|
|
75
|
+
opacity: 0;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
34
79
|
/* Controlled (non-<dialog>) usage. A portal/React modal that can't be a
|
|
35
80
|
native <dialog> wears the same skin + open layout via `.is-open`.
|
|
36
81
|
Backdrop, top-layer stacking and focus-trapping are then the
|
|
@@ -324,4 +369,10 @@
|
|
|
324
369
|
.ui-modal[open] {
|
|
325
370
|
animation: none;
|
|
326
371
|
}
|
|
372
|
+
|
|
373
|
+
/* No enter/exit transition — snap to the open state (no flash). */
|
|
374
|
+
dialog.ui-modal,
|
|
375
|
+
dialog.ui-modal::backdrop {
|
|
376
|
+
transition: none;
|
|
377
|
+
}
|
|
327
378
|
}
|