@pithyjs/pidl-catalog 0.1.0-beta.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/LICENSE +21 -0
- package/dist/specs/atoms/actions/button.pidl.json +289 -0
- package/dist/specs/atoms/actions/icon-group.pidl.json +141 -0
- package/dist/specs/atoms/actions/segmented-control.pidl.json +137 -0
- package/dist/specs/atoms/content/action-tile.pidl.json +135 -0
- package/dist/specs/atoms/content/badge.pidl.json +189 -0
- package/dist/specs/atoms/content/callout.pidl.json +170 -0
- package/dist/specs/atoms/content/code-block.pidl.json +170 -0
- package/dist/specs/atoms/content/heading.pidl.json +175 -0
- package/dist/specs/atoms/content/icon.pidl.json +127 -0
- package/dist/specs/atoms/content/image.pidl.json +227 -0
- package/dist/specs/atoms/content/prose.pidl.json +183 -0
- package/dist/specs/atoms/content/stat.pidl.json +158 -0
- package/dist/specs/atoms/content/tabs.pidl.json +207 -0
- package/dist/specs/atoms/content/text.pidl.json +212 -0
- package/dist/specs/atoms/content/tile.pidl.json +156 -0
- package/dist/specs/atoms/data/donut.pidl.json +352 -0
- package/dist/specs/atoms/data/gauge.pidl.json +274 -0
- package/dist/specs/atoms/forms/checkbox-group.pidl.json +170 -0
- package/dist/specs/atoms/forms/checkbox.pidl.json +323 -0
- package/dist/specs/atoms/forms/file-picker.pidl.json +161 -0
- package/dist/specs/atoms/forms/form-field.pidl.json +106 -0
- package/dist/specs/atoms/forms/input.pidl.json +466 -0
- package/dist/specs/atoms/forms/radio-group.pidl.json +174 -0
- package/dist/specs/atoms/forms/radio.pidl.json +312 -0
- package/dist/specs/atoms/forms/search-input.pidl.json +265 -0
- package/dist/specs/atoms/forms/select.pidl.json +420 -0
- package/dist/specs/atoms/forms/textarea.pidl.json +422 -0
- package/dist/specs/atoms/forms/toggle.pidl.json +314 -0
- package/dist/specs/layout/grid-item.pidl.json +120 -0
- package/dist/specs/layout/grid.pidl.json +157 -0
- package/dist/specs/layout/page-section.pidl.json +105 -0
- package/dist/specs/layout/smart-container.pidl.json +148 -0
- package/dist/specs/layout/stack.pidl.json +144 -0
- package/dist/specs/organisms/content/accordion.pidl.json +196 -0
- package/dist/specs/organisms/content/card.pidl.json +283 -0
- package/dist/specs/organisms/feedback/alert-dialog.pidl.json +158 -0
- package/dist/specs/organisms/feedback/modal.pidl.json +133 -0
- package/dist/specs/organisms/feedback/toast.pidl.json +150 -0
- package/dist/specs/organisms/forms/dynamic-form.pidl.json +314 -0
- package/dist/specs/organisms/forms/form.pidl.json +135 -0
- package/dist/specs/organisms/forms/subscribe-form.pidl.json +188 -0
- package/dist/specs/organisms/navigation/command-palette.pidl.json +263 -0
- package/dist/specs/organisms/navigation/docs-sidebar.pidl.json +264 -0
- package/dist/specs/organisms/navigation/navbar.pidl.json +356 -0
- package/dist/specs/organisms/navigation/table-of-contents.pidl.json +197 -0
- package/dist/specs/sections/contact-section.pidl.json +183 -0
- package/dist/specs/sections/cta-section.pidl.json +168 -0
- package/dist/specs/sections/faq-section.pidl.json +202 -0
- package/dist/specs/sections/features-section.pidl.json +214 -0
- package/dist/specs/sections/footer-section.pidl.json +187 -0
- package/dist/specs/sections/hero-section.pidl.json +202 -0
- package/dist/src/borders-psychology.d.ts +17 -0
- package/dist/src/borders-psychology.d.ts.map +1 -0
- package/dist/src/borders-psychology.js +42 -0
- package/dist/src/borders-psychology.js.map +1 -0
- package/dist/src/color-psychology.d.ts +20 -0
- package/dist/src/color-psychology.d.ts.map +1 -0
- package/dist/src/color-psychology.js +57 -0
- package/dist/src/color-psychology.js.map +1 -0
- package/dist/src/design-intelligence.d.ts +13 -0
- package/dist/src/design-intelligence.d.ts.map +1 -0
- package/dist/src/design-intelligence.js +36 -0
- package/dist/src/design-intelligence.js.map +1 -0
- package/dist/src/index.d.ts +6238 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +105 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/motion-psychology.d.ts +19 -0
- package/dist/src/motion-psychology.d.ts.map +1 -0
- package/dist/src/motion-psychology.js +47 -0
- package/dist/src/motion-psychology.js.map +1 -0
- package/dist/src/mutation-vocabulary.d.ts +13 -0
- package/dist/src/mutation-vocabulary.d.ts.map +1 -0
- package/dist/src/mutation-vocabulary.js +67 -0
- package/dist/src/mutation-vocabulary.js.map +1 -0
- package/dist/src/radius-psychology.d.ts +17 -0
- package/dist/src/radius-psychology.d.ts.map +1 -0
- package/dist/src/radius-psychology.js +42 -0
- package/dist/src/radius-psychology.js.map +1 -0
- package/dist/src/shadows-psychology.d.ts +17 -0
- package/dist/src/shadows-psychology.d.ts.map +1 -0
- package/dist/src/shadows-psychology.js +44 -0
- package/dist/src/shadows-psychology.js.map +1 -0
- package/dist/src/spacing-psychology.d.ts +22 -0
- package/dist/src/spacing-psychology.d.ts.map +1 -0
- package/dist/src/spacing-psychology.js +59 -0
- package/dist/src/spacing-psychology.js.map +1 -0
- package/dist/src/typography-psychology.d.ts +22 -0
- package/dist/src/typography-psychology.d.ts.map +1 -0
- package/dist/src/typography-psychology.js +68 -0
- package/dist/src/typography-psychology.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "component",
|
|
3
|
+
"id": "ui.donut",
|
|
4
|
+
"name": "Donut",
|
|
5
|
+
"displayName": "Donut Chart",
|
|
6
|
+
"version": "0.1.0",
|
|
7
|
+
"level": "atom",
|
|
8
|
+
"domain": "data",
|
|
9
|
+
"bidi": true,
|
|
10
|
+
"description": "Parts-of-a-whole chart drawn as arcs on one stroked ring. Each segment is a full <circle> showing a single dash of its own share, positioned by stroke-dasharray/stroke-dashoffset, with the ring rotated -90° so segment one starts at twelve o'clock. The SVG is aria-hidden; a visually-hidden data table carries every value and share and is the chart's accessible representation. A colour key is built in and on by default — a segment is identified by its colour and by nothing else, and nobody can name a colour. Loading, empty (including an all-zero total), and error states are first-class.",
|
|
11
|
+
"props": [
|
|
12
|
+
{
|
|
13
|
+
"name": "label",
|
|
14
|
+
"label": "Accessible name",
|
|
15
|
+
"type": "string",
|
|
16
|
+
"required": true,
|
|
17
|
+
"placeholder": "Claims by confidence",
|
|
18
|
+
"description": "Captions the visually-hidden data table screen readers read instead of the graphic. Required — it is the chart's accessible name."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "segments",
|
|
22
|
+
"label": "Segments",
|
|
23
|
+
"type": "DonutSegment[] | Signal<DonutSegment[]>",
|
|
24
|
+
"required": true,
|
|
25
|
+
"reactive": true,
|
|
26
|
+
"placeholder": [
|
|
27
|
+
{ "label": "Verified", "value": 420 },
|
|
28
|
+
{ "label": "Likely", "value": 260 },
|
|
29
|
+
{ "label": "Speculative", "value": 130 }
|
|
30
|
+
],
|
|
31
|
+
"description": "Slices in draw order. A value must be a finite number >= 0; anything else is counted as zero, warned about in DEV, and still listed in the accessible table.",
|
|
32
|
+
"itemSchema": {
|
|
33
|
+
"label": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"required": true,
|
|
36
|
+
"description": "Category name. Becomes the row header in the accessible table."
|
|
37
|
+
},
|
|
38
|
+
"value": {
|
|
39
|
+
"type": "number",
|
|
40
|
+
"required": true,
|
|
41
|
+
"description": "The quantity. Finite and >= 0."
|
|
42
|
+
},
|
|
43
|
+
"color": {
|
|
44
|
+
"type": "string",
|
|
45
|
+
"editor": { "type": "token-color" },
|
|
46
|
+
"enum": [
|
|
47
|
+
"primary",
|
|
48
|
+
"secondary",
|
|
49
|
+
"tertiary",
|
|
50
|
+
"success",
|
|
51
|
+
"info",
|
|
52
|
+
"warning",
|
|
53
|
+
"danger",
|
|
54
|
+
"categorical-1",
|
|
55
|
+
"categorical-2",
|
|
56
|
+
"categorical-3",
|
|
57
|
+
"categorical-4",
|
|
58
|
+
"categorical-5",
|
|
59
|
+
"categorical-6"
|
|
60
|
+
],
|
|
61
|
+
"description": "Design-token name for this slice. Omit to take the categorical palette slot for the segment's index — the correct default for identity encoding."
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"name": "total",
|
|
67
|
+
"label": "Centre value",
|
|
68
|
+
"type": "string | Signal<string>",
|
|
69
|
+
"reactive": true,
|
|
70
|
+
"placeholder": "1.2k",
|
|
71
|
+
"description": "Centre display value. Defaults to the formatted sum of the segments; override to show an abbreviated or externally computed figure."
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"name": "caption",
|
|
75
|
+
"label": "Centre caption",
|
|
76
|
+
"type": "string | Signal<string>",
|
|
77
|
+
"reactive": true,
|
|
78
|
+
"placeholder": "claims",
|
|
79
|
+
"description": "Small uppercase caption under the centre value. Omitted entirely when unset. Dropped once the drawing is narrower than 8rem, on one threshold shared by every size — a caption is words, and words stop being readable below a width that has nothing to do with how big the chart was asked to be. `size='sm'` caps AT 8rem, so an `sm` chart never renders a caption at any width; that is deliberate (it is the stat-tile size, where the number alone is the point and the data table still names the unit), and setting both now warns in DEV instead of failing silently."
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"name": "legend",
|
|
83
|
+
"label": "Colour key",
|
|
84
|
+
"editor": { "tab": "layout" },
|
|
85
|
+
"type": "enum",
|
|
86
|
+
"enum": ["end", "bottom", "none"],
|
|
87
|
+
"default": "end",
|
|
88
|
+
"description": "Where the colour key sits. 'end' puts it beside the ring and wraps it underneath when the row cannot hold both — intrinsically, with no breakpoint deciding. 'bottom' keeps it under the ring at every width. 'none' removes it, and is only correct when the colour-to-category mapping is carried elsewhere: a one-segment ring, or a row of donuts under one shared key."
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"name": "size",
|
|
92
|
+
"label": "Size",
|
|
93
|
+
"editor": { "tab": "layout" },
|
|
94
|
+
"type": "enum",
|
|
95
|
+
"enum": ["sm", "md", "lg"],
|
|
96
|
+
"default": "md",
|
|
97
|
+
"description": "Caps the chart's inline extent, and floors it at 6rem — below that the ring is thinner than its own stroke and no centre label fits inside it, so the chart stops shrinking and lets the container overflow rather than degrading where nobody notices. Intrinsically fluid between the two, with no breakpoints. When a colour key is shown the cap applies to the DRAWING rather than to the figure: capping the pair at the drawing's size would take the space out of the ring. `size` sets the centre label's CEILING; the label itself is sized `clamp(floor, Ncqi, ceiling)` against the drawing's own inline extent, so it can never outgrow the ring hole and a larger size can never produce smaller text at the same width."
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"name": "loading",
|
|
101
|
+
"label": "Loading",
|
|
102
|
+
"editor": { "tab": "settings" },
|
|
103
|
+
"type": "boolean | Signal<boolean>",
|
|
104
|
+
"default": false,
|
|
105
|
+
"reactive": true,
|
|
106
|
+
"description": "Hides the drawing and the table, shows a pulsing skeleton in the drawing's slot, and sets aria-busy on the drawing's wrapper. The colour key keeps its box and loses its text, so the chart does not resize when data arrives. Outranks every other state."
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"name": "error",
|
|
110
|
+
"label": "Error",
|
|
111
|
+
"editor": { "tab": "settings" },
|
|
112
|
+
"type": "boolean | Signal<boolean>",
|
|
113
|
+
"default": false,
|
|
114
|
+
"reactive": true,
|
|
115
|
+
"description": "Hides the graphic and the table and shows the error message. Outranks the empty state — a failed fetch usually leaves an empty array behind it."
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"name": "errorMessage",
|
|
119
|
+
"label": "Error message",
|
|
120
|
+
"editor": { "tab": "settings" },
|
|
121
|
+
"type": "string | Signal<string>",
|
|
122
|
+
"default": "Chart data unavailable",
|
|
123
|
+
"reactive": true,
|
|
124
|
+
"description": "Copy shown in the error state."
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"name": "emptyMessage",
|
|
128
|
+
"label": "Empty message",
|
|
129
|
+
"editor": { "tab": "settings" },
|
|
130
|
+
"type": "string",
|
|
131
|
+
"default": "No data to display",
|
|
132
|
+
"description": "Copy shown when there are no segments, or when every segment is zero."
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
"name": "loadingLabel",
|
|
136
|
+
"label": "Loading message",
|
|
137
|
+
"editor": { "tab": "settings" },
|
|
138
|
+
"type": "string",
|
|
139
|
+
"default": "Loading chart…",
|
|
140
|
+
"description": "Copy shown in the loading state."
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
"name": "id",
|
|
144
|
+
"label": "ID",
|
|
145
|
+
"editor": { "tab": "settings" },
|
|
146
|
+
"type": "string",
|
|
147
|
+
"description": "Explicit element ID on the <figure> root."
|
|
148
|
+
}
|
|
149
|
+
],
|
|
150
|
+
"events": [],
|
|
151
|
+
"slots": [],
|
|
152
|
+
"tokens": {
|
|
153
|
+
"track.stroke": "--color-surface-variant",
|
|
154
|
+
"segment.stroke": "--color-categorical-1 … --color-categorical-6",
|
|
155
|
+
"total.color": "--color-text",
|
|
156
|
+
"total.font": "--font-family-mono",
|
|
157
|
+
"total.size.ceiling": "--font-size-lg (the clamp's upper bound, not a fixed size)",
|
|
158
|
+
"total.size.floor": "--font-size-sm",
|
|
159
|
+
"caption.color": "--color-text-muted",
|
|
160
|
+
"caption.size": "--font-size-xs",
|
|
161
|
+
"legend.swatch.background": "--color-categorical-1 … --color-categorical-6 (the same token the swatch's own arc takes)",
|
|
162
|
+
"legend.name.color": "--color-text-muted",
|
|
163
|
+
"legend.value.color": "--color-text",
|
|
164
|
+
"legend.value.font": "--font-family-mono",
|
|
165
|
+
"legend.size": "--font-size-sm",
|
|
166
|
+
"legend.radius": "--radius-sm",
|
|
167
|
+
"legend.swatch.radius": "--radius-xs",
|
|
168
|
+
"legend.emphasis.background": "--color-surface-variant",
|
|
169
|
+
"legend.emphasis.motion": "--motion-transition-background",
|
|
170
|
+
"status.color": "--color-text-muted",
|
|
171
|
+
"status.error.color": "--color-danger",
|
|
172
|
+
"placeholder.background": "--color-surface-variant",
|
|
173
|
+
"placeholder.motion": "--motion-duration-slow"
|
|
174
|
+
},
|
|
175
|
+
"spacing": {
|
|
176
|
+
"internal": {
|
|
177
|
+
"graphic-to-status": { "relationship": "close_friends", "token": "sm" },
|
|
178
|
+
"total-to-caption": { "relationship": "lovers", "token": "xs" },
|
|
179
|
+
"graphic-to-legend": {
|
|
180
|
+
"relationship": "acquaintances",
|
|
181
|
+
"token": "lg",
|
|
182
|
+
"note": "Two different kinds of thing, read in turn. Drops to `md` when the key is placed below the ring, where it reads as a caption of the drawing."
|
|
183
|
+
},
|
|
184
|
+
"swatch-to-name": {
|
|
185
|
+
"relationship": "lovers",
|
|
186
|
+
"token": "xs",
|
|
187
|
+
"note": "A swatch means nothing apart from its name."
|
|
188
|
+
},
|
|
189
|
+
"legend-row-to-row": {
|
|
190
|
+
"relationship": "lovers",
|
|
191
|
+
"token": "xs",
|
|
192
|
+
"note": "Rows of one key. Each row also carries `xs` padding, so the hover background reads as a row rather than as a stripe behind the text."
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
"constraints": [
|
|
197
|
+
"Use logical properties only — no left/right, no width/height where inline-size/block-size applies",
|
|
198
|
+
"Colour is a design-token NAME, never a CSS value — there is no prop through which a hex code can reach a stylesheet",
|
|
199
|
+
"Series colour defaults to the categorical palette slot for the segment index; past the sixth slot the segment is painted neutral rather than repeating slot 1",
|
|
200
|
+
"The SVG is aria-hidden — the visually-hidden data table is the accessible representation, so `label` is required",
|
|
201
|
+
"A segment of zero is never drawn: with any linecap it would paint a stray mark for a share of nothing",
|
|
202
|
+
"A segment whose arc falls below half a user unit is omitted from the ring and kept in the table — clamping it up would push every later segment round the ring and misreport proportions",
|
|
203
|
+
"An all-zero total renders the empty state; the geometry never divides by zero",
|
|
204
|
+
"The drawing and the data table are hidden together in every non-ready state so the two cannot disagree about whether there is data. The KEY is not hidden with them: taking its height away resized the chart by 185px when data arrived, which is what the skeleton exists to prevent. It keeps its box and loses its text to skeleton bars, so it states nothing while reserving the space it will need",
|
|
205
|
+
"The chart's total is a <tfoot> row in the data table. The centre of the ring is aria-hidden, so without it the headline number reaches a screen reader nowhere at all — and under a `total` override it is not recoverable by summing the body rows either",
|
|
206
|
+
"A share that is greater than zero but rounds to zero reads '<0.1%', never '0%'. A component arguing that a chart must not round a segment out of the DATA cannot then report a segment that exists as nothing",
|
|
207
|
+
"An empty or whitespace-only segment label is a MISSING label and becomes 'Series N'. `??` let '' through and produced a blank row header and a blank key row — a category a reader can neither name nor look up",
|
|
208
|
+
"An overflowed total (finite values summing past Number.MAX_VALUE) renders the empty state. A total that is not a finite number is not a whole to take parts of, and the alternative was Infinity/Infinity — NaN in the markup of a chart still reporting itself ready",
|
|
209
|
+
"A multi-segment donut must carry a colour key — its own, or one shared on the page. Colour is the only thing identifying a segment (WCAG 1.4.1). `legend: 'none'` opts out of the built-in key, not out of the requirement",
|
|
210
|
+
"The key is text on screen, never a hover tooltip: hover has no touch and no keyboard equivalent, so a mapping that lives only there is a mapping some readers never get — and revealed content is then bound by WCAG 1.4.13",
|
|
211
|
+
"Labels are never drawn inside the arcs: the ring floors at 6rem, where arcs are a few device pixels tall, and the segments most in need of a label are the small ones with no room for one",
|
|
212
|
+
"Every segment gets a key row, including the ones the ring could not draw — a sub-pixel segment has no arc to point at and is exactly the one a reader cannot otherwise account for",
|
|
213
|
+
"One colour resolution feeds both the arc and its swatch; a key painted by a second call is a key that can disagree with the graphic, and disagree quietly",
|
|
214
|
+
"Hover emphasis grows the arc rather than dimming its neighbours — dimming a mark lowers its contrast against the surface, and WCAG 1.4.11 asks for 3:1 from every one of them"
|
|
215
|
+
],
|
|
216
|
+
"a11y": [
|
|
217
|
+
"SVG carries aria-hidden='true' and focusable='false' — it is decoration over the real data",
|
|
218
|
+
"A <table class='u-sr-only'> carries every value and share, captioned with the required `label` prop",
|
|
219
|
+
"Table headers are scoped: <th scope='col'> in the head, <th scope='row'> per segment",
|
|
220
|
+
"Zero-value and sub-pixel segments appear in the table even though they are not drawn",
|
|
221
|
+
"Status messages live in a role='status' region present from first render — a live region inserted when it gains content is often not announced",
|
|
222
|
+
"aria-busy='true' while loading goes on the drawing's wrapper (.ui-chart-body), NEVER on the root. The role='status' region is a child of the root, and a live region inside an aria-busy subtree has its changes DEFERRED until busy clears — which happens in the same call that blanks the region, so the announcement an AT ends up processing is 'empty'. Four charts loading announced nothing on the way in and nothing on the way out",
|
|
223
|
+
"A chart mounted ALREADY in a non-ready state does not announce that state: the first render is synchronous with construction, so the region carries its message before the caller has appended the chart, and a live region arriving with content reads as an insertion. Every state change after mount does announce",
|
|
224
|
+
"forced-color-adjust: none on the drawing — in a chart the colour IS the data, so flattening six slots to one system colour would delete the encoding; the table remains authoritative",
|
|
225
|
+
"Centre label is aria-hidden — it repeats the table's <tfoot> total, which is where a screen reader gets it",
|
|
226
|
+
"The <tfoot> row carries the caption as its unit, so it reads as a summary and not as one more category: 'Total builds, 1.2k, 100%'",
|
|
227
|
+
"The legend is aria-hidden for the same reason: the table already carries every category, value and share, and exposing both would read the dataset twice, the second time in a flatter form",
|
|
228
|
+
"Nothing inside the legend is focusable, which is what makes hiding it from the accessibility tree safe",
|
|
229
|
+
"Hover emphasis carries no information — every category and share is already on screen — so it is free to be pointer-only; no keyboard or touch reader loses anything by never triggering it",
|
|
230
|
+
"Legend swatches keep forced-color-adjust: none for the same reason the drawing does, and gain a CanvasText border so they stay visible against a forced background"
|
|
231
|
+
],
|
|
232
|
+
"ai": {
|
|
233
|
+
"synonyms": [
|
|
234
|
+
"donut chart",
|
|
235
|
+
"doughnut chart",
|
|
236
|
+
"ring chart",
|
|
237
|
+
"pie chart",
|
|
238
|
+
"breakdown chart",
|
|
239
|
+
"share of total",
|
|
240
|
+
"composition chart",
|
|
241
|
+
"chart legend",
|
|
242
|
+
"colour key",
|
|
243
|
+
"color key"
|
|
244
|
+
],
|
|
245
|
+
"avoid": [
|
|
246
|
+
"Passing a CSS colour — the color field takes a design-token name",
|
|
247
|
+
"Encoding meaning with the semantic palette for arbitrary categories (danger for 'category 3' says category 3 is bad)",
|
|
248
|
+
"More than six uncoloured segments — fold the tail into an explicit 'Other' bucket",
|
|
249
|
+
"Relying on the graphic alone for accessibility — the table is the accessible representation and `label` captions it",
|
|
250
|
+
"Using a donut to compare values across charts; use it for parts of one whole",
|
|
251
|
+
"Setting legend='none' on a multi-segment donut with no other key on the page — that leaves colour as the sole carrier of meaning",
|
|
252
|
+
"Hand-rolling a legend beside the component: the swatch has to resolve its token exactly as the arc does, and a separately built key can drift from the graphic",
|
|
253
|
+
"Asking for a hover tooltip to carry the category names — it is unreachable by touch and by keyboard, and the key is already on screen"
|
|
254
|
+
],
|
|
255
|
+
"patterns": [
|
|
256
|
+
"Basic: <Donut label=\"'Claims by confidence'\" segments=\"claims\" />",
|
|
257
|
+
"With centre caption: <Donut label=\"'Claims'\" segments=\"claims\" caption=\"'claims'\" />",
|
|
258
|
+
"Explicit colours: segments=[{ label: 'Passed', value: 900, color: 'success' }]",
|
|
259
|
+
"Live: <Donut label=\"'Traffic'\" segments=\"() => sources()\" loading=\"isFetching\" />",
|
|
260
|
+
"Abbreviated total: <Donut label=\"'Claims'\" segments=\"claims\" total=\"'1.2k'\" />",
|
|
261
|
+
"Key under the ring in a narrow column: <Donut label=\"'Claims'\" segments=\"claims\" legend=\"'bottom'\" />",
|
|
262
|
+
"Key suppressed because one is shared across a row of donuts: <Donut label=\"'Claims'\" segments=\"claims\" legend=\"'none'\" />"
|
|
263
|
+
]
|
|
264
|
+
},
|
|
265
|
+
"examples": [
|
|
266
|
+
{
|
|
267
|
+
"id": "basic",
|
|
268
|
+
"title": "Three-segment breakdown",
|
|
269
|
+
"pithy": "<Donut label=\"'Claims by confidence'\" segments=\"claims\" />",
|
|
270
|
+
"assertions": [
|
|
271
|
+
"figure.ui-chart.ui-donut with data-status='ready'",
|
|
272
|
+
"one .ui-donut-segment per segment plus one .ui-donut-track",
|
|
273
|
+
"data-color runs categorical-1, categorical-2, categorical-3",
|
|
274
|
+
"no axe violations"
|
|
275
|
+
]
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
"id": "zero-total",
|
|
279
|
+
"title": "Every segment is zero",
|
|
280
|
+
"pithy": "<Donut label=\"'Claims'\" segments=\"allZero\" />",
|
|
281
|
+
"assertions": [
|
|
282
|
+
"data-status='empty'",
|
|
283
|
+
"no NaN or Infinity anywhere in the markup",
|
|
284
|
+
"no arcs drawn"
|
|
285
|
+
]
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
"id": "sub-pixel",
|
|
289
|
+
"title": "A segment too small to draw",
|
|
290
|
+
"pithy": "<Donut label=\"'Long tail'\" segments=\"[{label:'Bulk',value:100000},{label:'Speck',value:1}]\" />",
|
|
291
|
+
"assertions": [
|
|
292
|
+
"one arc drawn, two table rows",
|
|
293
|
+
"the omitted segment keeps its exact value in the table"
|
|
294
|
+
]
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
"id": "legend-default",
|
|
298
|
+
"title": "The colour key is on by default",
|
|
299
|
+
"pithy": "<Donut label=\"'Claims by confidence'\" segments=\"claims\" />",
|
|
300
|
+
"assertions": [
|
|
301
|
+
"the figure carries data-legend='end'",
|
|
302
|
+
"one .ui-chart-legend-item per segment, in mark order",
|
|
303
|
+
"each swatch's data-color equals its own arc's data-color",
|
|
304
|
+
"the legend is aria-hidden='true', and the table still carries the numbers once"
|
|
305
|
+
]
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
"id": "legend-keys-an-undrawn-segment",
|
|
309
|
+
"title": "A segment too small to draw still gets a key row",
|
|
310
|
+
"pithy": "<Donut label=\"'Long tail'\" segments=\"[{label:'Bulk',value:100000},{label:'Speck',value:1}]\" />",
|
|
311
|
+
"assertions": [
|
|
312
|
+
"one arc drawn, two legend rows, two table rows",
|
|
313
|
+
"the key row for the undrawn segment carries its exact value and share"
|
|
314
|
+
]
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
"id": "legend-none",
|
|
318
|
+
"title": "No key",
|
|
319
|
+
"pithy": "<Donut label=\"'Claims'\" segments=\"claims\" legend=\"'none'\" />",
|
|
320
|
+
"assertions": [
|
|
321
|
+
"no data-legend attribute and no .ui-chart-legend",
|
|
322
|
+
"hovering an arc sets no emphasis — there is nothing to pair it with"
|
|
323
|
+
]
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
"id": "loading",
|
|
327
|
+
"title": "Loading",
|
|
328
|
+
"pithy": "<Donut label=\"'Claims'\" segments=\"claims\" loading=\"true\" />",
|
|
329
|
+
"assertions": [
|
|
330
|
+
"aria-busy='true' on .ui-chart-body, and absent from the root",
|
|
331
|
+
"graphic and table both hidden",
|
|
332
|
+
"role='status' region reads 'Loading chart…'"
|
|
333
|
+
]
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
"id": "error",
|
|
337
|
+
"title": "Error outranks empty",
|
|
338
|
+
"pithy": "<Donut label=\"'Claims'\" segments=\"[]\" error=\"true\" errorMessage=\"'Upstream timed out'\" />",
|
|
339
|
+
"assertions": [
|
|
340
|
+
"data-status='error'",
|
|
341
|
+
"the error message is shown, not the empty message"
|
|
342
|
+
]
|
|
343
|
+
}
|
|
344
|
+
],
|
|
345
|
+
"designGuidance": {
|
|
346
|
+
"surface": "No background of its own — the ring sits on whatever surface hosts it. The track uses --color-surface-variant so the unfilled remainder reads as a groove rather than a hole.",
|
|
347
|
+
"depth": "Flat. No shadow: a donut's depth cue is the ring's own thickness, and a drop shadow on a stroked arc muddies the boundary between adjacent slices.",
|
|
348
|
+
"borders": "None on the figure. In forced-colors the drawing opts out of colour substitution (forced-color-adjust: none) because the colour is the data; the placeholder gains a 1px CanvasText border so the reserved space stays visible.",
|
|
349
|
+
"interactivity": "Hover pairs a key row with its arc, in both directions: the arc thickens, the row takes a surface-variant background. That is emphasis, not disclosure — it restates what the key already says, which is precisely why it may be pointer-only. Nothing here is a route to information a keyboard or touch reader would otherwise miss, and nothing appears that WCAG 1.4.13 would then have to govern. Emphasis grows the arc rather than dimming its neighbours, so no mark's contrast against the surface ever drops. The centre label stays pointer-events: none so it can never intercept the hover.",
|
|
350
|
+
"bestPractices": "Use for parts of one whole, not for comparing across charts. Keep to six or fewer categories and fold the rest into 'Other' — the categorical palette guarantees separation for six. Leave colour unset unless a category has a genuine semantic (passed/failed); identity encoding is what the default is for. Keep the key: it is the only thing that turns a colour into a category, and the palette's CVD work guarantees the slots are DISTINGUISHABLE from each other, never that any one of them is identifiable. Reach for legend='bottom' in a narrow column, and legend='none' only when a shared key sits nearby. Always give a specific `label`: it is the caption a screen-reader user hears. Prefer an explicit `emptyMessage` that says what is missing ('No claims indexed yet') over the generic default."
|
|
351
|
+
}
|
|
352
|
+
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "component",
|
|
3
|
+
"id": "ui.gauge",
|
|
4
|
+
"name": "Gauge",
|
|
5
|
+
"displayName": "Gauge Chart",
|
|
6
|
+
"version": "0.1.0",
|
|
7
|
+
"level": "atom",
|
|
8
|
+
"domain": "data",
|
|
9
|
+
"bidi": true,
|
|
10
|
+
"description": "One value's position within a range, drawn on a half circle. A full-length track path sits under a value path revealed by stroke-dasharray, so a short arc reads as 'short out of this much' rather than just 'short'. The arc is πr — half a circle, not a full one. The SVG is aria-hidden; a visually-hidden table carries the reading, the domain and the share of it. Colour is a prop: threshold-to-colour is application policy, not component behaviour.",
|
|
11
|
+
"props": [
|
|
12
|
+
{
|
|
13
|
+
"name": "label",
|
|
14
|
+
"label": "Accessible name",
|
|
15
|
+
"type": "string",
|
|
16
|
+
"required": true,
|
|
17
|
+
"placeholder": "Overall confidence",
|
|
18
|
+
"description": "Captions the visually-hidden data table screen readers read instead of the graphic, and names the reading's row. Required — it is the chart's accessible name."
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"name": "value",
|
|
22
|
+
"label": "Value",
|
|
23
|
+
"type": "number | Signal<number>",
|
|
24
|
+
"required": true,
|
|
25
|
+
"reactive": true,
|
|
26
|
+
"placeholder": 55,
|
|
27
|
+
"description": "The reading. Clamped into the domain rather than allowed to overdraw — a gauge showing 140% would wrap past its own start and read as 40%. A non-finite value renders the error state, not a reading of zero."
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"name": "min",
|
|
31
|
+
"label": "Minimum",
|
|
32
|
+
"editor": { "tab": "settings" },
|
|
33
|
+
"type": "number | Signal<number>",
|
|
34
|
+
"default": 0,
|
|
35
|
+
"reactive": true,
|
|
36
|
+
"description": "Domain floor."
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"name": "max",
|
|
40
|
+
"label": "Maximum",
|
|
41
|
+
"editor": { "tab": "settings" },
|
|
42
|
+
"type": "number | Signal<number>",
|
|
43
|
+
"default": 100,
|
|
44
|
+
"reactive": true,
|
|
45
|
+
"description": "Domain ceiling. Must be greater than min; a degenerate or inverted domain renders the error state instead of dividing by zero."
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"name": "color",
|
|
49
|
+
"label": "Colour",
|
|
50
|
+
"editor": { "type": "token-color" },
|
|
51
|
+
"type": "enum",
|
|
52
|
+
"enum": [
|
|
53
|
+
"primary",
|
|
54
|
+
"secondary",
|
|
55
|
+
"tertiary",
|
|
56
|
+
"success",
|
|
57
|
+
"info",
|
|
58
|
+
"warning",
|
|
59
|
+
"danger",
|
|
60
|
+
"categorical-1",
|
|
61
|
+
"categorical-2",
|
|
62
|
+
"categorical-3",
|
|
63
|
+
"categorical-4",
|
|
64
|
+
"categorical-5",
|
|
65
|
+
"categorical-6"
|
|
66
|
+
],
|
|
67
|
+
"default": "primary",
|
|
68
|
+
"reactive": true,
|
|
69
|
+
"description": "Design-token name for the value arc. The component never picks a colour from the value — the same 30% is excellent for a defect rate and dire for uptime, so the threshold policy belongs to the call site. Reactive, because that policy is only useful if the colour follows the reading: pass an accessor and the arc recolours as the value crosses a threshold. It was documented as live and resolved once at construction, so a gauge sweeping from 40 to 92 kept its first colour the whole way."
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"name": "caption",
|
|
73
|
+
"label": "Caption",
|
|
74
|
+
"type": "string | Signal<string>",
|
|
75
|
+
"reactive": true,
|
|
76
|
+
"placeholder": "verified",
|
|
77
|
+
"description": "Small uppercase caption under the percentage. Omitted entirely when unset. Dropped once the drawing is narrower than 8rem, on one threshold shared by every size — a caption is words, and words stop being readable below a width that has nothing to do with how big the chart was asked to be. `size='sm'` caps AT 8rem, so an `sm` chart never renders a caption at any width; that is deliberate (it is the stat-tile size, where the number alone is the point and the data table still names the unit), and setting both now warns in DEV instead of failing silently."
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"name": "size",
|
|
81
|
+
"label": "Size",
|
|
82
|
+
"editor": { "tab": "layout" },
|
|
83
|
+
"type": "enum",
|
|
84
|
+
"enum": ["sm", "md", "lg"],
|
|
85
|
+
"default": "md",
|
|
86
|
+
"description": "Caps the chart's inline extent, and floors it at 6rem — below that no centre label fits over the arc at any legible size, so the chart stops shrinking and lets the container overflow rather than degrading where nobody notices. Intrinsically fluid between the two, with no breakpoints. `size` sets the label's CEILING; the label itself is sized against the drawing, so a larger size can never produce smaller text at the same width."
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"name": "loading",
|
|
90
|
+
"label": "Loading",
|
|
91
|
+
"editor": { "tab": "settings" },
|
|
92
|
+
"type": "boolean | Signal<boolean>",
|
|
93
|
+
"default": false,
|
|
94
|
+
"reactive": true,
|
|
95
|
+
"description": "Hides the arc and the table, shows a pulsing skeleton in the drawing's slot, and sets aria-busy on the drawing's wrapper. Outranks every other state."
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"name": "error",
|
|
99
|
+
"label": "Error",
|
|
100
|
+
"editor": { "tab": "settings" },
|
|
101
|
+
"type": "boolean | Signal<boolean>",
|
|
102
|
+
"default": false,
|
|
103
|
+
"reactive": true,
|
|
104
|
+
"description": "Hides the graphic and the table and shows the error message. A domain that makes no sense sets this state on its own."
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"name": "errorMessage",
|
|
108
|
+
"label": "Error message",
|
|
109
|
+
"editor": { "tab": "settings" },
|
|
110
|
+
"type": "string | Signal<string>",
|
|
111
|
+
"default": "Chart data unavailable",
|
|
112
|
+
"reactive": true,
|
|
113
|
+
"description": "Copy shown in the error state."
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
"name": "empty",
|
|
117
|
+
"label": "Empty",
|
|
118
|
+
"editor": { "tab": "settings" },
|
|
119
|
+
"type": "boolean | Signal<boolean>",
|
|
120
|
+
"default": false,
|
|
121
|
+
"reactive": true,
|
|
122
|
+
"description": "Force the empty state — there is no reading yet, as distinct from a reading of zero. A gauge cannot infer this: 0 is a legitimate value."
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"name": "emptyMessage",
|
|
126
|
+
"label": "Empty message",
|
|
127
|
+
"editor": { "tab": "settings" },
|
|
128
|
+
"type": "string",
|
|
129
|
+
"default": "No data to display",
|
|
130
|
+
"description": "Copy shown in the empty state."
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"name": "loadingLabel",
|
|
134
|
+
"label": "Loading message",
|
|
135
|
+
"editor": { "tab": "settings" },
|
|
136
|
+
"type": "string",
|
|
137
|
+
"default": "Loading chart…",
|
|
138
|
+
"description": "Copy shown in the loading state."
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"name": "id",
|
|
142
|
+
"label": "ID",
|
|
143
|
+
"editor": { "tab": "settings" },
|
|
144
|
+
"type": "string",
|
|
145
|
+
"description": "Explicit element ID on the <figure> root."
|
|
146
|
+
}
|
|
147
|
+
],
|
|
148
|
+
"events": [],
|
|
149
|
+
"slots": [],
|
|
150
|
+
"tokens": {
|
|
151
|
+
"track.stroke": "--color-surface-variant",
|
|
152
|
+
"value.stroke": "--color-primary (or any semantic / categorical token)",
|
|
153
|
+
"percent.color": "--color-text",
|
|
154
|
+
"percent.font": "--font-family-mono",
|
|
155
|
+
"percent.size.ceiling": "--font-size-lg (the clamp's upper bound, not a fixed size)",
|
|
156
|
+
"percent.size.floor": "--font-size-sm",
|
|
157
|
+
"caption.color": "--color-text-muted",
|
|
158
|
+
"caption.size": "--font-size-xs",
|
|
159
|
+
"value.motion": "--motion-duration-normal",
|
|
160
|
+
"value.easing": "--motion-easing-standard",
|
|
161
|
+
"status.error.color": "--color-danger"
|
|
162
|
+
},
|
|
163
|
+
"spacing": {
|
|
164
|
+
"internal": {
|
|
165
|
+
"graphic-to-status": { "relationship": "close_friends", "token": "sm" },
|
|
166
|
+
"percent-to-caption": { "relationship": "lovers", "token": "xs" }
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
"constraints": [
|
|
170
|
+
"Use logical properties only",
|
|
171
|
+
"The arc is πr, not 2πr — a half circle",
|
|
172
|
+
"Colour is a design-token NAME chosen by the caller, never derived from the value inside the component",
|
|
173
|
+
"The SVG is aria-hidden — the visually-hidden data table is the accessible representation, so `label` is required",
|
|
174
|
+
"No value path is drawn while the percentage rounds to zero. The arc wears a ROUND linecap, which renders any dash as a capsule one stroke width longer than itself — a 0.578-unit arc (value 0.4 of 100) painted a ~9-unit blob beside a label reading '0%'. A graphic that contradicts its own label inside one component is worse than one that omits a reading too small to show",
|
|
175
|
+
"A degenerate domain (max <= min) or a non-finite value renders the error state — presenting broken configuration as a reading of zero would be a lie",
|
|
176
|
+
"The value path is reused across renders so its dash length genuinely interpolates; the sweep respects prefers-reduced-motion",
|
|
177
|
+
"The arc is SVG and scales with its viewBox; the centre label is HTML and does not. `size` is the author's intent, fixed when the component is written, so it sets a CEILING and the label is sized `clamp(floor, Ncqi, ceiling)` against the drawing's own inline extent — no rungs, and no schedule that can fire on one size and not another. The caption is the exception: it is dropped on one absolute threshold shared by every size, because a sentence's minimum width has nothing to do with how big the chart was asked to be"
|
|
178
|
+
],
|
|
179
|
+
"a11y": [
|
|
180
|
+
"SVG carries aria-hidden='true' and focusable='false'",
|
|
181
|
+
"A <table class='u-sr-only'> gives the reading, the minimum, the maximum and the share of range — a value without its range means nothing",
|
|
182
|
+
"Table headers are scoped: <th scope='col'> in the head, <th scope='row'> per measure",
|
|
183
|
+
"Status messages live in a role='status' region present from first render",
|
|
184
|
+
"aria-busy='true' while loading goes on the drawing's wrapper (.ui-chart-body), NEVER on the root. The role='status' region is a child of the root, and a live region inside an aria-busy subtree has its changes DEFERRED until busy clears — which happens in the same call that blanks the region, so the announcement an AT ends up processing is 'empty'. Four charts loading announced nothing on the way in and nothing on the way out",
|
|
185
|
+
"A chart mounted ALREADY in a non-ready state does not announce that state: the first render is synchronous with construction, so the region carries its message before the caller has appended the chart, and a live region arriving with content reads as an insertion. Every state change after mount does announce",
|
|
186
|
+
"forced-color-adjust: none on the drawing so the value arc stays distinguishable from the track; the table remains authoritative",
|
|
187
|
+
"The centre percentage is aria-hidden — it repeats the table's share of range",
|
|
188
|
+
"Colour is never the only channel: the arc length carries the reading, and the exact number is in the table"
|
|
189
|
+
],
|
|
190
|
+
"ai": {
|
|
191
|
+
"synonyms": [
|
|
192
|
+
"gauge",
|
|
193
|
+
"half donut",
|
|
194
|
+
"semicircle chart",
|
|
195
|
+
"dial",
|
|
196
|
+
"speedometer",
|
|
197
|
+
"progress arc",
|
|
198
|
+
"score meter",
|
|
199
|
+
"percentage ring"
|
|
200
|
+
],
|
|
201
|
+
"avoid": [
|
|
202
|
+
"Expecting the component to colour itself by threshold — compute the token name at the call site and pass it in",
|
|
203
|
+
"Passing a CSS colour instead of a token name",
|
|
204
|
+
"Using a gauge for several series — it shows exactly one reading",
|
|
205
|
+
"Leaving min/max at 0–100 when the real domain is something else; the accessible table publishes the domain and a wrong one misinforms",
|
|
206
|
+
"Using `value: 0` to mean 'no reading' — set `empty` instead"
|
|
207
|
+
],
|
|
208
|
+
"patterns": [
|
|
209
|
+
"Basic: <Gauge label=\"'Overall confidence'\" value=\"score\" />",
|
|
210
|
+
"With caption: <Gauge label=\"'Confidence'\" value=\"score\" caption=\"'verified'\" />",
|
|
211
|
+
"Caller-side threshold policy, live: color=\"() => score() > 80 ? 'success' : 'warning'\"",
|
|
212
|
+
"Custom domain: <Gauge label=\"'Latency budget'\" value=\"150\" min=\"100\" max=\"200\" />",
|
|
213
|
+
"No reading yet: <Gauge label=\"'Confidence'\" value=\"0\" empty=\"true\" />"
|
|
214
|
+
]
|
|
215
|
+
},
|
|
216
|
+
"examples": [
|
|
217
|
+
{
|
|
218
|
+
"id": "basic",
|
|
219
|
+
"title": "A reading out of 100",
|
|
220
|
+
"pithy": "<Gauge label=\"'Overall confidence'\" value=\"55\" />",
|
|
221
|
+
"assertions": [
|
|
222
|
+
"figure.ui-chart.ui-gauge with data-status='ready'",
|
|
223
|
+
"track and value paths share the same d",
|
|
224
|
+
"stroke-dasharray is 'πr×0.55 πr'",
|
|
225
|
+
"centre reads '55%'",
|
|
226
|
+
"no axe violations"
|
|
227
|
+
]
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
"id": "explicit-colour",
|
|
231
|
+
"title": "Colour supplied by the call site",
|
|
232
|
+
"pithy": "<Gauge label=\"'Confidence'\" value=\"92\" color=\"'success'\" />",
|
|
233
|
+
"assertions": [
|
|
234
|
+
"value path carries data-color='success'",
|
|
235
|
+
"the same component with value=5 and color='success' is still green — no threshold logic"
|
|
236
|
+
]
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
"id": "zero",
|
|
240
|
+
"title": "A reading of zero",
|
|
241
|
+
"pithy": "<Gauge label=\"'Confidence'\" value=\"0\" />",
|
|
242
|
+
"assertions": [
|
|
243
|
+
"no .ui-gauge-value element at all",
|
|
244
|
+
"the track is still drawn",
|
|
245
|
+
"centre reads '0%'"
|
|
246
|
+
]
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
"id": "degenerate-domain",
|
|
250
|
+
"title": "max equals min",
|
|
251
|
+
"pithy": "<Gauge label=\"'Broken'\" value=\"5\" min=\"10\" max=\"10\" />",
|
|
252
|
+
"assertions": [
|
|
253
|
+
"data-status='error'",
|
|
254
|
+
"no NaN or Infinity anywhere in the markup"
|
|
255
|
+
]
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"id": "custom-domain",
|
|
259
|
+
"title": "A domain that is not 0–100",
|
|
260
|
+
"pithy": "<Gauge label=\"'Latency budget'\" value=\"150\" min=\"100\" max=\"200\" />",
|
|
261
|
+
"assertions": [
|
|
262
|
+
"centre reads '50%'",
|
|
263
|
+
"table rows publish Minimum 100 and Maximum 200"
|
|
264
|
+
]
|
|
265
|
+
}
|
|
266
|
+
],
|
|
267
|
+
"designGuidance": {
|
|
268
|
+
"surface": "No background of its own. The track uses --color-surface-variant, which is what turns the value arc from 'a short arc' into 'a short arc out of this much'. Never drop the track to save ink — without it the reading has no scale.",
|
|
269
|
+
"depth": "Flat. No shadow on the arc: the stroke width is the depth cue, and a shadow blurs the boundary between track and value.",
|
|
270
|
+
"borders": "None on the figure. In forced-colors the drawing opts out of colour substitution so the value arc stays distinguishable from the track, and the placeholder gains a CanvasText border.",
|
|
271
|
+
"interactivity": "Non-interactive. The value arc transitions its dash length under motion-safe, so a changing reading sweeps rather than jumps; the centre label is pointer-events: none.",
|
|
272
|
+
"bestPractices": "Set min/max to the real domain — the accessible table publishes them, so a default 0–100 on a scale that is not 0–100 misinforms a screen-reader user directly. Choose the colour at the call site with an explicit threshold function, and keep that function in one place so two gauges of the same metric cannot disagree. Pair a gauge with a caption naming the unit — at `md` or `lg`, since `sm` never renders one and the component warns in DEV if you try. Keep that caption to a couple of words: the Gliora design puts a sentence there, which cannot wrap inside the arc at any size the component offers. Use `empty` for 'no reading yet' rather than showing a zero that looks like a measurement."
|
|
273
|
+
}
|
|
274
|
+
}
|