@ponchia/ui 0.5.0 → 0.6.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 +322 -0
- package/MIGRATIONS.json +14 -0
- package/README.md +28 -5
- package/annotations/index.d.ts +398 -276
- package/annotations/index.d.ts.map +1 -0
- package/annotations/index.js +315 -45
- package/behaviors/carousel.js +17 -16
- package/behaviors/combobox.js +47 -16
- package/behaviors/command.js +18 -15
- package/behaviors/connectors.js +4 -5
- package/behaviors/crosshair.js +4 -5
- package/behaviors/dialog.js +3 -2
- package/behaviors/disclosure.js +3 -2
- package/behaviors/dismissible.js +3 -2
- package/behaviors/forms.js +41 -13
- package/behaviors/glyph.js +4 -5
- package/behaviors/internal.js +47 -0
- package/behaviors/legend.js +23 -2
- package/behaviors/menu.js +3 -2
- package/behaviors/popover.js +78 -7
- package/behaviors/spotlight.js +4 -5
- package/behaviors/table.js +39 -12
- package/behaviors/tabs.js +14 -14
- package/behaviors/theme.js +5 -3
- package/behaviors/toast.js +13 -1
- package/classes/classes.json +1857 -0
- package/classes/index.d.ts +28 -13
- package/classes/index.js +34 -18
- package/classes/vscode.css-custom-data.json +12 -0
- package/connectors/index.d.ts +189 -69
- package/connectors/index.d.ts.map +1 -0
- package/connectors/index.js +120 -24
- package/css/app.css +43 -13
- package/css/base.css +15 -10
- package/css/connectors.css +17 -0
- package/css/content.css +7 -1
- package/css/dataviz.css +5 -1
- package/css/disclosure.css +38 -6
- package/css/dots.css +57 -0
- package/css/feedback.css +60 -2
- package/css/forms.css +42 -1
- package/css/legend.css +11 -7
- package/css/marks.css +38 -8
- package/css/motion.css +24 -44
- package/css/navigation.css +7 -0
- package/css/overlay.css +31 -1
- package/css/primitives.css +91 -5
- package/css/report.css +40 -63
- package/css/site.css +16 -2
- package/css/sources.css +43 -1
- package/css/spotlight.css +1 -1
- package/css/tokens.css +36 -1
- package/css/workbench.css +1 -1
- package/dist/bronto.css +1 -1
- package/dist/css/analytical.css +1 -1
- package/dist/css/app.css +1 -1
- package/dist/css/base.css +1 -1
- package/dist/css/connectors.css +1 -1
- package/dist/css/content.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/legend.css +1 -1
- package/dist/css/marks.css +1 -1
- package/dist/css/motion.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.css +1 -1
- package/dist/css/site.css +1 -1
- package/dist/css/sources.css +1 -1
- package/dist/css/spotlight.css +1 -1
- package/dist/css/tokens.css +1 -1
- package/dist/css/workbench.css +1 -1
- package/docs/adr/0003-theme-model.md +1 -1
- package/docs/annotations.md +94 -14
- package/docs/architecture.md +50 -6
- package/docs/contrast.md +116 -92
- package/docs/d2.md +195 -0
- package/docs/legends.md +18 -2
- package/docs/marks.md +9 -2
- package/docs/mermaid.md +152 -0
- package/docs/reference.md +78 -22
- package/docs/reporting.md +395 -57
- package/docs/sources.md +27 -0
- package/docs/stability.md +9 -2
- package/docs/usage.md +101 -4
- package/docs/vega.md +225 -0
- package/docs/workbench.md +7 -1
- package/glyphs/glyphs.js +6 -4
- package/llms.txt +139 -14
- package/package.json +50 -12
- package/qwik/index.d.ts +42 -59
- package/qwik/index.d.ts.map +1 -0
- package/qwik/index.js +55 -3
- package/react/index.d.ts +39 -61
- package/react/index.d.ts.map +1 -0
- package/react/index.js +57 -3
- package/solid/index.d.ts +64 -61
- package/solid/index.d.ts.map +1 -0
- package/solid/index.js +60 -3
- package/tokens/d2.d.ts +38 -0
- package/tokens/d2.js +71 -0
- package/tokens/d2.json +43 -0
- package/tokens/index.d.ts +5 -5
- package/tokens/index.js +15 -1
- package/tokens/index.json +9 -0
- package/tokens/mermaid.d.ts +23 -0
- package/tokens/mermaid.js +181 -0
- package/tokens/mermaid.json +163 -0
- package/tokens/resolved.json +45 -1
- package/tokens/skins.js +3 -2
- package/tokens/tokens.dtcg.json +26 -0
- package/tokens/vega.d.ts +34 -0
- package/tokens/vega.js +155 -0
- package/tokens/vega.json +179 -0
package/connectors/index.js
CHANGED
|
@@ -10,36 +10,78 @@
|
|
|
10
10
|
*
|
|
11
11
|
* import { connectRects } from '@ponchia/ui/connectors';
|
|
12
12
|
* const { d } = connectRects({ fromRect: a, toRect: b, shape: 'elbow' });
|
|
13
|
+
*
|
|
14
|
+
* The public types below are JSDoc `@typedef`s; the shipped `index.d.ts` is
|
|
15
|
+
* generated from them (and these signatures) by `tsc --emitDeclarationOnly`.
|
|
16
|
+
*
|
|
17
|
+
* @typedef {{ x: number, y: number }} Point
|
|
18
|
+
* @typedef {{ x: number, y: number, width: number, height: number }} Rect
|
|
19
|
+
* @typedef {'top' | 'right' | 'bottom' | 'left' | 'center'} Side
|
|
20
|
+
* @typedef {'straight' | 'elbow' | 'curve'} ConnectorShape
|
|
21
|
+
*
|
|
22
|
+
* @typedef {object} ConnectorPathOptions
|
|
23
|
+
* @property {Point} from
|
|
24
|
+
* @property {Point} to
|
|
25
|
+
* @property {ConnectorShape} [shape]
|
|
26
|
+
* @property {number} [curvature] Curve control-point reach along the dominant axis (curve shape). Default 0.5.
|
|
27
|
+
* @property {number} [mid] Turn position 0..1 along the span (elbow shape). Default 0.5.
|
|
28
|
+
*
|
|
29
|
+
* @typedef {object} ConnectRectsOptions
|
|
30
|
+
* @property {Rect} fromRect
|
|
31
|
+
* @property {Rect} toRect
|
|
32
|
+
* @property {Side} [fromSide] Anchor edges. Omit both to auto-pick facing edges from the rects.
|
|
33
|
+
* @property {Side} [toSide]
|
|
34
|
+
* @property {ConnectorShape} [shape]
|
|
35
|
+
* @property {number} [curvature]
|
|
36
|
+
* @property {number} [mid]
|
|
37
|
+
*
|
|
38
|
+
* @typedef {object} ConnectRectsResult
|
|
39
|
+
* @property {string} d SVG path data.
|
|
40
|
+
* @property {Point} from
|
|
41
|
+
* @property {Point} to
|
|
42
|
+
* @property {number} angle The path's end-tangent at `to` in radians — the direction the path arrives, so rotating an arrowhead at `to` by this points it along the path. Equals the straight `from`→`to` angle for `shape: 'straight'`; axis-aligned for `elbow`/`curve`.
|
|
13
43
|
*/
|
|
14
44
|
|
|
15
|
-
|
|
45
|
+
// Shared scalar/geometry primitives. Exported so the annotations layer composes
|
|
46
|
+
// on the SAME kernel instead of copy-pasting it (the copies had silently
|
|
47
|
+
// diverged — see `clamp`). Low-level helpers; the documented API is the path
|
|
48
|
+
// builders below.
|
|
49
|
+
export const PRECISION = 1000;
|
|
16
50
|
|
|
17
|
-
function finite(name, value, fallback) {
|
|
51
|
+
export function finite(name, value, fallback) {
|
|
18
52
|
const v = value ?? fallback;
|
|
19
53
|
if (!Number.isFinite(v)) throw new TypeError(`${name} must be a finite number`);
|
|
20
54
|
return v;
|
|
21
55
|
}
|
|
22
56
|
|
|
23
|
-
function dimension(name, value, fallback) {
|
|
57
|
+
export function dimension(name, value, fallback) {
|
|
24
58
|
const v = finite(name, value, fallback);
|
|
25
59
|
if (v < 0) throw new RangeError(`${name} must be greater than or equal to 0`);
|
|
26
60
|
return v;
|
|
27
61
|
}
|
|
28
62
|
|
|
29
|
-
function fmt(value) {
|
|
63
|
+
export function fmt(value) {
|
|
30
64
|
const rounded = Math.round((Object.is(value, -0) ? 0 : value) * PRECISION) / PRECISION;
|
|
31
65
|
return String(Object.is(rounded, -0) ? 0 : rounded);
|
|
32
66
|
}
|
|
33
67
|
|
|
34
|
-
function point(x, y) {
|
|
68
|
+
export function point(x, y) {
|
|
35
69
|
return `${fmt(x)},${fmt(y)}`;
|
|
36
70
|
}
|
|
37
71
|
|
|
38
|
-
|
|
72
|
+
// Guarded form (returns min when the range is inverted) — the reconciled body;
|
|
73
|
+
// connectors only ever calls clamp(v, 0, 1) so this is output-identical here.
|
|
74
|
+
export function clamp(value, min, max) {
|
|
75
|
+
if (max < min) return min;
|
|
39
76
|
return Math.min(max, Math.max(min, value));
|
|
40
77
|
}
|
|
41
78
|
|
|
42
|
-
/**
|
|
79
|
+
/**
|
|
80
|
+
* A point on a rect's edge (or centre). `rect` is `{ x, y, width, height }`.
|
|
81
|
+
* @param {Rect} rect
|
|
82
|
+
* @param {Side} [side]
|
|
83
|
+
* @returns {Point}
|
|
84
|
+
*/
|
|
43
85
|
export function anchorPoint(rect, side = 'center') {
|
|
44
86
|
const x = finite('rect.x', rect?.x, 0);
|
|
45
87
|
const y = finite('rect.y', rect?.y, 0);
|
|
@@ -60,7 +102,12 @@ export function anchorPoint(rect, side = 'center') {
|
|
|
60
102
|
}
|
|
61
103
|
}
|
|
62
104
|
|
|
63
|
-
/**
|
|
105
|
+
/**
|
|
106
|
+
* Angle (radians) from `from` to `to`.
|
|
107
|
+
* @param {Point} from
|
|
108
|
+
* @param {Point} to
|
|
109
|
+
* @returns {number}
|
|
110
|
+
*/
|
|
64
111
|
export function angleBetween(from, to) {
|
|
65
112
|
return Math.atan2(
|
|
66
113
|
finite('to.y', to?.y) - finite('from.y', from?.y),
|
|
@@ -68,6 +115,12 @@ export function angleBetween(from, to) {
|
|
|
68
115
|
);
|
|
69
116
|
}
|
|
70
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Straight line from `from` to `to`.
|
|
120
|
+
* @param {Point} from
|
|
121
|
+
* @param {Point} to
|
|
122
|
+
* @returns {string}
|
|
123
|
+
*/
|
|
71
124
|
export function straightPath(from, to) {
|
|
72
125
|
return `M${point(finite('from.x', from?.x), finite('from.y', from?.y))}L${point(
|
|
73
126
|
finite('to.x', to?.x),
|
|
@@ -75,7 +128,13 @@ export function straightPath(from, to) {
|
|
|
75
128
|
)}`;
|
|
76
129
|
}
|
|
77
130
|
|
|
78
|
-
/**
|
|
131
|
+
/**
|
|
132
|
+
* Right-angle dogleg. Turns on the dominant axis at `mid` (0..1) of the span.
|
|
133
|
+
* @param {Point} from
|
|
134
|
+
* @param {Point} to
|
|
135
|
+
* @param {{ mid?: number }} [opts]
|
|
136
|
+
* @returns {string}
|
|
137
|
+
*/
|
|
79
138
|
export function elbowPath(from, to, opts = {}) {
|
|
80
139
|
const fx = finite('from.x', from?.x);
|
|
81
140
|
const fy = finite('from.y', from?.y);
|
|
@@ -92,7 +151,13 @@ export function elbowPath(from, to, opts = {}) {
|
|
|
92
151
|
return `M${point(fx, fy)}V${fmt(my)}H${fmt(tx)}V${fmt(ty)}`;
|
|
93
152
|
}
|
|
94
153
|
|
|
95
|
-
/**
|
|
154
|
+
/**
|
|
155
|
+
* Cubic curve; control points extend along the dominant axis by `curvature`.
|
|
156
|
+
* @param {Point} from
|
|
157
|
+
* @param {Point} to
|
|
158
|
+
* @param {{ curvature?: number }} [opts]
|
|
159
|
+
* @returns {string}
|
|
160
|
+
*/
|
|
96
161
|
export function curvePath(from, to, opts = {}) {
|
|
97
162
|
const fx = finite('from.x', from?.x);
|
|
98
163
|
const fy = finite('from.y', from?.y);
|
|
@@ -107,7 +172,11 @@ export function curvePath(from, to, opts = {}) {
|
|
|
107
172
|
return `M${point(fx, fy)}C${point(c1.x, c1.y)} ${point(c2.x, c2.y)} ${point(tx, ty)}`;
|
|
108
173
|
}
|
|
109
174
|
|
|
110
|
-
/**
|
|
175
|
+
/**
|
|
176
|
+
* Build a path between two points by `shape` (`straight` | `elbow` | `curve`).
|
|
177
|
+
* @param {ConnectorPathOptions} [opts]
|
|
178
|
+
* @returns {string}
|
|
179
|
+
*/
|
|
111
180
|
export function connectorPath(opts = {}) {
|
|
112
181
|
const { from, to, shape = 'straight' } = opts;
|
|
113
182
|
if (shape === 'elbow') return elbowPath(from, to, opts);
|
|
@@ -115,20 +184,34 @@ export function connectorPath(opts = {}) {
|
|
|
115
184
|
return straightPath(from, to);
|
|
116
185
|
}
|
|
117
186
|
|
|
118
|
-
/**
|
|
119
|
-
|
|
187
|
+
/**
|
|
188
|
+
* A filled triangle arrowhead at `p`, pointing along `angle` (radians).
|
|
189
|
+
* @param {Point} p
|
|
190
|
+
* @param {number} angle
|
|
191
|
+
* @param {number} [size]
|
|
192
|
+
* @param {number} [spread] Half-angle of the head in radians (default 0.45).
|
|
193
|
+
* Smaller is crisper/sharper; must be in (0, π/2).
|
|
194
|
+
* @returns {string}
|
|
195
|
+
*/
|
|
196
|
+
export function arrowHead(p, angle, size = 8, spread = 0.45) {
|
|
120
197
|
const px = finite('p.x', p?.x);
|
|
121
198
|
const py = finite('p.y', p?.y);
|
|
122
199
|
const a = finite('angle', angle, 0);
|
|
123
200
|
const s = dimension('size', size, 8);
|
|
201
|
+
const sp = finite('spread', spread, 0.45);
|
|
202
|
+
if (sp <= 0 || sp >= Math.PI / 2) throw new RangeError('spread must be in (0, π/2)');
|
|
124
203
|
const back = a + Math.PI;
|
|
125
|
-
const
|
|
126
|
-
const
|
|
127
|
-
const p2 = { x: px + Math.cos(back + spread) * s, y: py + Math.sin(back + spread) * s };
|
|
204
|
+
const p1 = { x: px + Math.cos(back - sp) * s, y: py + Math.sin(back - sp) * s };
|
|
205
|
+
const p2 = { x: px + Math.cos(back + sp) * s, y: py + Math.sin(back + sp) * s };
|
|
128
206
|
return `M${point(px, py)}L${point(p1.x, p1.y)}L${point(p2.x, p2.y)}Z`;
|
|
129
207
|
}
|
|
130
208
|
|
|
131
|
-
/**
|
|
209
|
+
/**
|
|
210
|
+
* A filled dot at `p`.
|
|
211
|
+
* @param {Point} p
|
|
212
|
+
* @param {number} [radius]
|
|
213
|
+
* @returns {string}
|
|
214
|
+
*/
|
|
132
215
|
export function dotMark(p, radius = 3) {
|
|
133
216
|
const px = finite('p.x', p?.x);
|
|
134
217
|
const py = finite('p.y', p?.y);
|
|
@@ -139,7 +222,12 @@ export function dotMark(p, radius = 3) {
|
|
|
139
222
|
)} 0 1 1 ${point(px, py - r)}Z`;
|
|
140
223
|
}
|
|
141
224
|
|
|
142
|
-
/**
|
|
225
|
+
/**
|
|
226
|
+
* Pick facing edges from the rects' relative centres.
|
|
227
|
+
* @param {Rect} fromRect
|
|
228
|
+
* @param {Rect} toRect
|
|
229
|
+
* @returns {{ from: Side, to: Side }}
|
|
230
|
+
*/
|
|
143
231
|
export function autoSides(fromRect, toRect) {
|
|
144
232
|
const fc = anchorPoint(fromRect, 'center');
|
|
145
233
|
const tc = anchorPoint(toRect, 'center');
|
|
@@ -152,13 +240,14 @@ export function autoSides(fromRect, toRect) {
|
|
|
152
240
|
}
|
|
153
241
|
|
|
154
242
|
/**
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
243
|
+
* Angle (radians) at which a `shape` path *arrives* at `to` — straight is the
|
|
244
|
+
* chord; elbow/curve arrive axis-aligned along the dominant axis. Rotate an
|
|
245
|
+
* end marker by this so it points along the path, not the chord.
|
|
246
|
+
* @param {Point} from
|
|
247
|
+
* @param {Point} to
|
|
248
|
+
* @param {ConnectorShape} [shape]
|
|
249
|
+
* @returns {number}
|
|
158
250
|
*/
|
|
159
|
-
/** Angle (radians) at which a `shape` path *arrives* at `to` — straight is the
|
|
160
|
-
* chord; elbow/curve arrive axis-aligned along the dominant axis. Rotate an
|
|
161
|
-
* end marker by this so it points along the path, not the chord. */
|
|
162
251
|
export function endTangentAngle(from, to, shape = 'straight') {
|
|
163
252
|
if (shape === 'straight') return angleBetween(from, to);
|
|
164
253
|
const dx = finite('to.x', to?.x) - finite('from.x', from?.x);
|
|
@@ -167,6 +256,13 @@ export function endTangentAngle(from, to, shape = 'straight') {
|
|
|
167
256
|
return dy >= 0 ? Math.PI / 2 : -Math.PI / 2;
|
|
168
257
|
}
|
|
169
258
|
|
|
259
|
+
/**
|
|
260
|
+
* Connect two rects. Resolves anchor points (explicit `fromSide`/`toSide`, else
|
|
261
|
+
* auto), builds the path, and returns `{ d, from, to, angle }` so the caller can
|
|
262
|
+
* place an arrowhead/dot at `to` rotated by `angle`.
|
|
263
|
+
* @param {ConnectRectsOptions} [opts]
|
|
264
|
+
* @returns {ConnectRectsResult}
|
|
265
|
+
*/
|
|
170
266
|
export function connectRects(opts = {}) {
|
|
171
267
|
const { fromRect, toRect, shape = 'straight', curvature, mid } = opts;
|
|
172
268
|
// Honor each side override independently; auto-pick whichever is unset.
|
package/css/app.css
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
.ui-app-shell {
|
|
7
7
|
display: grid;
|
|
8
8
|
grid-template-columns: var(--app-rail, 14rem) minmax(0, 1fr);
|
|
9
|
-
min-block-size:
|
|
9
|
+
min-block-size: 100dvh;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
.ui-app-shell--full {
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
padding: var(--space-md);
|
|
25
25
|
position: sticky;
|
|
26
26
|
inset-block-start: 0;
|
|
27
|
-
block-size:
|
|
27
|
+
block-size: 100svh;
|
|
28
28
|
overflow-y: auto;
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
display: flex;
|
|
35
35
|
font-family: var(--display);
|
|
36
36
|
font-size: 1.05rem;
|
|
37
|
+
font-weight: var(--display-weight);
|
|
37
38
|
gap: 0.5rem;
|
|
38
39
|
letter-spacing: var(--tracking-wide);
|
|
39
40
|
padding: 0.35rem 0.5rem;
|
|
@@ -87,18 +88,33 @@
|
|
|
87
88
|
inline-size: 0.34rem;
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
@media (hover: hover) {
|
|
92
|
+
.ui-app-nav a:hover {
|
|
93
|
+
background: var(--bg-accent);
|
|
94
|
+
color: var(--text);
|
|
95
|
+
}
|
|
93
96
|
}
|
|
94
97
|
|
|
95
|
-
|
|
98
|
+
/* Current page honours BOTH the `.is-active` class and `aria-current` — the
|
|
99
|
+
sibling `.ui-sitenav` signals current-page with `aria-current="page"`, so the
|
|
100
|
+
app-shell nav now accepts the same programmatic cue (author it for AT, not
|
|
101
|
+
just the visual class — C19). */
|
|
102
|
+
.ui-app-nav a.is-active,
|
|
103
|
+
.ui-app-nav a[aria-current]:not([aria-current='false']) {
|
|
96
104
|
background: var(--accent-soft);
|
|
97
105
|
border-inline-start-color: var(--accent);
|
|
98
106
|
color: var(--accent-text);
|
|
99
107
|
}
|
|
100
108
|
|
|
101
|
-
|
|
109
|
+
@media (pointer: coarse) {
|
|
110
|
+
.ui-app-nav a {
|
|
111
|
+
min-block-size: 2.9rem;
|
|
112
|
+
padding-inline: 0.9rem;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.ui-app-nav a.is-active::before,
|
|
117
|
+
.ui-app-nav a[aria-current]:not([aria-current='false'])::before {
|
|
102
118
|
opacity: 1;
|
|
103
119
|
}
|
|
104
120
|
|
|
@@ -153,6 +169,7 @@
|
|
|
153
169
|
color: var(--text);
|
|
154
170
|
font-family: var(--display);
|
|
155
171
|
font-size: 1.1rem;
|
|
172
|
+
font-weight: var(--display-weight);
|
|
156
173
|
letter-spacing: var(--tracking-wide);
|
|
157
174
|
margin: 0;
|
|
158
175
|
text-transform: uppercase;
|
|
@@ -207,6 +224,7 @@
|
|
|
207
224
|
color: var(--text);
|
|
208
225
|
font-family: var(--display);
|
|
209
226
|
font-size: 0.95rem;
|
|
227
|
+
font-weight: var(--display-weight);
|
|
210
228
|
letter-spacing: var(--tracking-wide);
|
|
211
229
|
margin: 0;
|
|
212
230
|
text-transform: uppercase;
|
|
@@ -230,11 +248,7 @@
|
|
|
230
248
|
permanent admin-shell alias (grouped on the canonical rules there —
|
|
231
249
|
identical output). Nothing app-specific left to define here. */
|
|
232
250
|
|
|
233
|
-
/* --- Mobile rail
|
|
234
|
-
|
|
235
|
-
.ui-app-rail__toggle {
|
|
236
|
-
display: none;
|
|
237
|
-
}
|
|
251
|
+
/* --- Mobile rail: collapses to a horizontal scrolling strip --- */
|
|
238
252
|
|
|
239
253
|
@media (max-width: 880px) {
|
|
240
254
|
.ui-app-shell {
|
|
@@ -254,6 +268,15 @@
|
|
|
254
268
|
display: none;
|
|
255
269
|
}
|
|
256
270
|
|
|
271
|
+
/* The rail is flex-row here, so the base `margin-block-start: auto` (which
|
|
272
|
+
pushed account to the bottom in the column layout) is inert and account can
|
|
273
|
+
scroll off. Push it to the inline end instead so sign-out stays reachable.
|
|
274
|
+
(layout review C21.) */
|
|
275
|
+
.ui-app-rail__account {
|
|
276
|
+
margin-block-start: 0;
|
|
277
|
+
margin-inline-start: auto;
|
|
278
|
+
}
|
|
279
|
+
|
|
257
280
|
.ui-app-nav {
|
|
258
281
|
grid-auto-flow: column;
|
|
259
282
|
gap: 0.15rem;
|
|
@@ -269,11 +292,18 @@
|
|
|
269
292
|
white-space: nowrap;
|
|
270
293
|
}
|
|
271
294
|
|
|
272
|
-
.ui-app-nav a.is-active
|
|
295
|
+
.ui-app-nav a.is-active,
|
|
296
|
+
.ui-app-nav a[aria-current]:not([aria-current='false']) {
|
|
273
297
|
border-inline-start: 0;
|
|
274
298
|
border-block-end-color: var(--accent);
|
|
275
299
|
}
|
|
276
300
|
|
|
301
|
+
/* No --app-rail-height token exists, so drop the redundant second sticky;
|
|
302
|
+
only the horizontal rail stays pinned. */
|
|
303
|
+
.ui-app-topbar {
|
|
304
|
+
position: static;
|
|
305
|
+
}
|
|
306
|
+
|
|
277
307
|
.ui-app-content {
|
|
278
308
|
padding: var(--space-md);
|
|
279
309
|
}
|
package/css/base.css
CHANGED
|
@@ -12,7 +12,10 @@ html {
|
|
|
12
12
|
background: var(--bg);
|
|
13
13
|
color: var(--text);
|
|
14
14
|
font-family: var(--sans);
|
|
15
|
-
|
|
15
|
+
|
|
16
|
+
/* = 15px at the 16px default, but rem honours a user's browser font-size
|
|
17
|
+
preference (which a px root would ignore). */
|
|
18
|
+
font-size: 0.9375rem;
|
|
16
19
|
line-height: 1.55;
|
|
17
20
|
scroll-behavior: smooth;
|
|
18
21
|
text-rendering: optimizeLegibility;
|
|
@@ -24,12 +27,16 @@ body {
|
|
|
24
27
|
background: var(--bg);
|
|
25
28
|
color: var(--text);
|
|
26
29
|
margin: 0;
|
|
27
|
-
min-block-size:
|
|
30
|
+
min-block-size: 100dvh;
|
|
28
31
|
position: relative;
|
|
29
32
|
}
|
|
30
33
|
|
|
34
|
+
:root {
|
|
35
|
+
--scroll-offset: 6rem;
|
|
36
|
+
}
|
|
37
|
+
|
|
31
38
|
main [id] {
|
|
32
|
-
scroll-margin-
|
|
39
|
+
scroll-margin-block-start: var(--scroll-offset);
|
|
33
40
|
}
|
|
34
41
|
|
|
35
42
|
::selection {
|
|
@@ -176,6 +183,7 @@ textarea:focus-visible,
|
|
|
176
183
|
|
|
177
184
|
.ui-display {
|
|
178
185
|
font-family: var(--display);
|
|
186
|
+
font-weight: var(--display-weight);
|
|
179
187
|
text-transform: uppercase;
|
|
180
188
|
}
|
|
181
189
|
|
|
@@ -219,13 +227,10 @@ textarea:focus-visible,
|
|
|
219
227
|
outline-offset: -2px;
|
|
220
228
|
}
|
|
221
229
|
|
|
222
|
-
/*
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
border-block-end-color: Highlight;
|
|
227
|
-
color: Highlight;
|
|
228
|
-
}
|
|
230
|
+
/* NB: the active-tab forced-colors re-assert lives in disclosure.css, right
|
|
231
|
+
after the `.ui-tab.is-active` default — placing it here (an earlier bundle
|
|
232
|
+
leaf) let the later default win even in forced-colors mode, since @media
|
|
233
|
+
adds no specificity. (a11y review C10.) */
|
|
229
234
|
|
|
230
235
|
/* Keyboard focus must never depend on a colour that gets overridden. */
|
|
231
236
|
a:focus-visible,
|
package/css/connectors.css
CHANGED
|
@@ -91,3 +91,20 @@
|
|
|
91
91
|
fill: CanvasText;
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
|
+
|
|
95
|
+
/* Print: keep the relationship lines and arrowheads in their meaning-carrying
|
|
96
|
+
colour, and settle any draw-on to a solid line (the draw needs JS to set
|
|
97
|
+
pathLength, which never runs for a static PDF). */
|
|
98
|
+
@media print {
|
|
99
|
+
.ui-connector__path,
|
|
100
|
+
.ui-connector__end {
|
|
101
|
+
-webkit-print-color-adjust: exact;
|
|
102
|
+
print-color-adjust: exact;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.ui-connector--draw .ui-connector__path {
|
|
106
|
+
animation: none;
|
|
107
|
+
stroke-dasharray: none;
|
|
108
|
+
stroke-dashoffset: 0;
|
|
109
|
+
}
|
|
110
|
+
}
|
package/css/content.css
CHANGED
|
@@ -13,6 +13,11 @@
|
|
|
13
13
|
font-size: var(--text-base);
|
|
14
14
|
line-height: 1.7;
|
|
15
15
|
|
|
16
|
+
/* Machine-generated Markdown carries long unbreakable tokens (URLs, hashes,
|
|
17
|
+
code) that otherwise force horizontal page scroll on the prose surface
|
|
18
|
+
itself. Break them. (css-robustness review C9; the table cell already does.) */
|
|
19
|
+
overflow-wrap: break-word;
|
|
20
|
+
|
|
16
21
|
/* Readable measure; the container can still be wider for tables/media. */
|
|
17
22
|
--prose-measure: 72ch;
|
|
18
23
|
}
|
|
@@ -75,7 +80,7 @@
|
|
|
75
80
|
|
|
76
81
|
/* Anchored headings clear a sticky chrome and expose a dot on hover. */
|
|
77
82
|
.ui-prose :is(h1, h2, h3, h4, h5, h6)[id] {
|
|
78
|
-
scroll-margin-block-start:
|
|
83
|
+
scroll-margin-block-start: var(--scroll-offset);
|
|
79
84
|
}
|
|
80
85
|
|
|
81
86
|
/* --- Text-level --- */
|
|
@@ -300,6 +305,7 @@
|
|
|
300
305
|
display: grid;
|
|
301
306
|
font-family: var(--display);
|
|
302
307
|
font-size: var(--text-xl);
|
|
308
|
+
font-weight: var(--display-weight);
|
|
303
309
|
gap: var(--space-sm);
|
|
304
310
|
line-height: 1.2;
|
|
305
311
|
margin-block: var(--space-xl);
|
package/css/dataviz.css
CHANGED
|
@@ -30,7 +30,11 @@
|
|
|
30
30
|
--chart-div-6: oklch(66% 0.13 55deg);
|
|
31
31
|
--chart-div-7: oklch(56% 0.15 45deg);
|
|
32
32
|
|
|
33
|
-
/* Dot-matrix pattern fills — pair with the matching colour (WCAG 1.4.1).
|
|
33
|
+
/* Dot-matrix pattern fills — pair with the matching colour (WCAG 1.4.1).
|
|
34
|
+
Series 1 (the accent) is intentionally `none`: absence-of-pattern IS its
|
|
35
|
+
redundant channel. The colour palette is CVD-safe on its own (gated by
|
|
36
|
+
check:charts), so a colour+pattern chart stays distinguishable; a
|
|
37
|
+
pattern-ONLY chart must still give series 1 a fill or a labelled legend. */
|
|
34
38
|
--chart-pattern-size: 8px;
|
|
35
39
|
--chart-pattern-ink: rgb(0 0 0 / 0.34);
|
|
36
40
|
--chart-pattern-1: none;
|
package/css/disclosure.css
CHANGED
|
@@ -44,11 +44,29 @@
|
|
|
44
44
|
border-color var(--duration-fast) var(--ease-standard);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
/* Coarse-pointer tap-target floor (~2.9rem ≈ 44px), matching inputs/sitenav —
|
|
48
|
+
tabs are easy to mis-tap on touch (WCAG 2.5.8 — C24). */
|
|
49
|
+
@media (pointer: coarse) {
|
|
50
|
+
.ui-tab {
|
|
51
|
+
min-block-size: 2.9rem;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
47
55
|
.ui-tab.is-active {
|
|
48
56
|
border-block-end-color: var(--accent);
|
|
49
57
|
color: var(--accent-text);
|
|
50
58
|
}
|
|
51
59
|
|
|
60
|
+
/* Forced-colors re-assert MUST sit after the default above (same specificity,
|
|
61
|
+
@media adds none) or the accent default wins and the selected tab loses its
|
|
62
|
+
only cue. Moved here from base.css, an earlier bundle leaf. (a11y review C10.) */
|
|
63
|
+
@media (forced-colors: active) {
|
|
64
|
+
.ui-tab.is-active {
|
|
65
|
+
border-block-end-color: Highlight;
|
|
66
|
+
color: Highlight;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
52
70
|
.ui-tab:focus-visible {
|
|
53
71
|
/* An inset box, deliberately distinct from the .is-active bottom-border
|
|
54
72
|
so focus ≠ selection for low-vision keyboard users: the global
|
|
@@ -199,7 +217,7 @@
|
|
|
199
217
|
|
|
200
218
|
.ui-segmented__option:has(input:focus-visible) {
|
|
201
219
|
outline: 2px solid var(--focus-ring);
|
|
202
|
-
outline-offset: 2px;
|
|
220
|
+
outline-offset: -2px;
|
|
203
221
|
}
|
|
204
222
|
|
|
205
223
|
@media (hover: hover) {
|
|
@@ -240,7 +258,7 @@
|
|
|
240
258
|
inline-size: 0.22rem;
|
|
241
259
|
}
|
|
242
260
|
|
|
243
|
-
.ui-breadcrumb__item[aria-current] {
|
|
261
|
+
.ui-breadcrumb__item[aria-current]:not([aria-current='false']) {
|
|
244
262
|
color: var(--text);
|
|
245
263
|
}
|
|
246
264
|
|
|
@@ -290,18 +308,32 @@
|
|
|
290
308
|
color: var(--accent-text);
|
|
291
309
|
}
|
|
292
310
|
|
|
293
|
-
|
|
311
|
+
/* Disabled covers BOTH the native `<button disabled>` the demo ships and the
|
|
312
|
+
`aria-disabled="true"` ARIA path; `pointer-events: none` makes the
|
|
313
|
+
aria-disabled item genuinely inert (it was clickable before — C5). */
|
|
314
|
+
.ui-pagination__item[aria-disabled='true'],
|
|
315
|
+
.ui-pagination__item:disabled {
|
|
294
316
|
cursor: not-allowed;
|
|
295
317
|
opacity: 0.4;
|
|
318
|
+
pointer-events: none;
|
|
296
319
|
}
|
|
297
320
|
|
|
298
321
|
@media (hover: hover) {
|
|
299
|
-
.ui-pagination__item:not(.is-active, [aria-disabled='true']):hover {
|
|
322
|
+
.ui-pagination__item:not(.is-active, [aria-disabled='true'], :disabled):hover {
|
|
300
323
|
border-color: var(--line-strong);
|
|
301
324
|
color: var(--text);
|
|
302
325
|
}
|
|
303
326
|
}
|
|
304
327
|
|
|
328
|
+
/* Coarse-pointer tap-target floor, matching inputs/sitenav (~2.9rem ≈ 44px) —
|
|
329
|
+
pagination controls are easy to mis-tap on touch (WCAG 2.5.8 — C24). */
|
|
330
|
+
@media (pointer: coarse) {
|
|
331
|
+
.ui-pagination__item {
|
|
332
|
+
min-block-size: 2.9rem;
|
|
333
|
+
min-inline-size: 2.9rem;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
305
337
|
/* --- Avatar — sharp, monospace initials or image --- */
|
|
306
338
|
|
|
307
339
|
.ui-avatar {
|
|
@@ -429,8 +461,8 @@
|
|
|
429
461
|
.ui-carousel__prev::before,
|
|
430
462
|
.ui-carousel__next::before {
|
|
431
463
|
block-size: 0.5rem;
|
|
432
|
-
border-block-start: 1.5px solid
|
|
433
|
-
border-inline-end: 1.5px solid
|
|
464
|
+
border-block-start: 1.5px solid currentcolor;
|
|
465
|
+
border-inline-end: 1.5px solid currentcolor;
|
|
434
466
|
content: '';
|
|
435
467
|
inline-size: 0.5rem;
|
|
436
468
|
}
|
package/css/dots.css
CHANGED
|
@@ -281,6 +281,13 @@
|
|
|
281
281
|
transform: rotate(315deg) translateY(calc(var(--ds-box) / -2 + var(--ds-dot) / 2));
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
+
/* The comet expects exactly 8 `<i>`; a 9th+ child has no rotation rule and
|
|
285
|
+
would pile up dead-centre. Hide the overflow so extra children fail safe
|
|
286
|
+
rather than rendering a stray static dot (C26). */
|
|
287
|
+
.ui-dotspinner i:nth-child(n + 9) {
|
|
288
|
+
display: none;
|
|
289
|
+
}
|
|
290
|
+
|
|
284
291
|
.ui-dotspinner--sm {
|
|
285
292
|
--ds-box: 1.05rem;
|
|
286
293
|
--ds-dot: 0.18rem;
|
|
@@ -370,3 +377,53 @@
|
|
|
370
377
|
clip-path: none;
|
|
371
378
|
}
|
|
372
379
|
}
|
|
380
|
+
|
|
381
|
+
/* Forced-colors (Windows HCM): semantic status dots and hot/accent matrix
|
|
382
|
+
cells encode meaning purely via background-color, which collapses to one
|
|
383
|
+
system colour under HCM (WCAG 1.4.1). Opt out of forced-color remapping and
|
|
384
|
+
apply four visually-distinct system colours so the signals stay
|
|
385
|
+
differentiable. */
|
|
386
|
+
@media (forced-colors: active) {
|
|
387
|
+
.ui-dot--success {
|
|
388
|
+
forced-color-adjust: none;
|
|
389
|
+
background: LinkText;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.ui-dot--warning {
|
|
393
|
+
forced-color-adjust: none;
|
|
394
|
+
background: Mark;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.ui-dot--danger {
|
|
398
|
+
forced-color-adjust: none;
|
|
399
|
+
background: Highlight;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.ui-dot--info {
|
|
403
|
+
forced-color-adjust: none;
|
|
404
|
+
background: ButtonText;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
.ui-dotmatrix__cell--hot {
|
|
408
|
+
forced-color-adjust: none;
|
|
409
|
+
background: Highlight;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.ui-dotmatrix__cell--accent {
|
|
413
|
+
forced-color-adjust: none;
|
|
414
|
+
background: LinkText;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/* Print: the dot surfaces carry data (heatmap cells, the segmented meter, the
|
|
419
|
+
status dot, the masked glyph), so their painted fills must survive the print
|
|
420
|
+
"economy" default that drops backgrounds. */
|
|
421
|
+
@media print {
|
|
422
|
+
.ui-dotmatrix__cell,
|
|
423
|
+
.ui-dotbar i,
|
|
424
|
+
.ui-dot,
|
|
425
|
+
.ui-icon {
|
|
426
|
+
-webkit-print-color-adjust: exact;
|
|
427
|
+
print-color-adjust: exact;
|
|
428
|
+
}
|
|
429
|
+
}
|