Components
Data viz
Numbers with personality. These four primitives put a value right where a sentence needs it — a bold arc, a friendly bar, a tiny trend — without ever turning the page into a dashboard. Four primitives, all Beta: Gauge (the hero), SeverityBars, Sparkline, and ProgressArc.
This is a reading layer, not a charting library. There are deliberately no axis grids, no legends, no tooltips, no multi-series — when a surface needs those, that's a real charting layer's job. These primitives put one number (or one small set) where a sentence needs it.
Gauge — the arc meter
The hero primitive: a simple, friendly arc meter. In this system the gauge is a rounded gold arc over a soft track — no needle, no engraved tick marks (those belong to the proposed system's instrument register). On mount the arc draws up to the reading on the --spinner-* timing; value updates glide smoothly. The number is always printed below — the arc never carries the value alone.
Severity tones remap the accent and pair the ADR-0017 glyph beside the reading — a severity is never color alone.
SeverityBars — the distribution
Findings by severity, one bar per tone. Every row carries the full ADR-0017 triple: the severity glyph, a text label naming the severity, and the color — color is never the sole signal, so strip the hue and the rows still read. The glyph set follows the ADR: octagon-alert for critical (the universal stop-sign silhouette). The shipped Beta messengers still render circle-x there — a named gap on /icons; new primitives adopt the ADR glyph rather than propagate the gap.
Sparkline — the word-sized trend
A trend line the width of a word, for sitting next to the number it explains. Straight segments — it draws what was measured, no smoothing. Here the line itself takes the accent color, heavier and rounded, and the latest reading lands a dot. (The ink-line-plus-engraved-baseline treatment belongs to the proposed system.) A sparkline is never the sole carrier of a value: the numbers it sketches must also exist as text, and its label must say what the trend does.
Open findings are trending down from 14 in March to 3 this week.
ProgressArc — the closing ring
A ring that fills clockwise from 12 o'clock — bold, rounded, easy to read at a glance. Two semantics, one prop: kind="progress" is work moving toward done (role progressbar); kind="score" is a standing measurement (role meter) — a readiness score is not "loading."
Two brands, two instruments
Under the official brand these primitives are flat material charts: solid rounded pill bars, a friendly gold arc, rounded caps, soft tracks — no needle, no engraving, no metallic finish (those belong to the proposed system's instrument register, one toggle away). Same markup either way; the primitives' own CSS gates the whole difference, so callers change nothing.
Accessibility
- Real semantics. Gauge is role="meter"; ProgressArc is role="progressbar" or role="meter" by kind; Sparkline is role="img" with a trend-describing label; SeverityBars is a real list whose rows read "label, count."
- Values are text. Every reading is printed and carried in aria-valuetext; no value is conveyed by geometry or color alone.
- Severity is never color alone. Every severity tone pairs the ADR-0017 glyph, and SeverityBars adds a text label naming the severity.
- Reduced motion. The settle, draw, and grow animations drop under prefers-reduced-motion (and the explicit data-motion="reduced" override); everything renders at rest on the value.
- Contrast. Severity accents lift to their -300 highlights on the dark plate; the reading text always carries the value in the page text register, so the colored geometry is supplementary.
API
Gauge
- value · min? · max?
- The reading and its scale; clamped, defaults 0–100.
- label: string
- Visible label under the reading; the meter's accessible name.
- reading? · unit?
- Formatted reading text (defaults to the rounded value) and a unit suffix; together they become aria-valuetext.
- tone?: VizTone = 'default'
- default (instrument gold) or info / affirm / caution / critical; severity tones pair the ADR-0017 glyph beside the reading.
- size?: number = 160
- Rendered square size in px.
SeverityBars
- items: { tone, label, value }[]
- Rows in display order; tone picks the glyph and the fill, label names the severity in text.
- label: string
- The distribution's accessible name.
- max?: number
- Scale ceiling; defaults to the largest item so the top severity reads full-width.
- formatValue?: (n) => string
- Formatted count text per row.
Sparkline
- data: number[]
- The series, oldest first.
- label: string
- Required accessible description — say what the trend does, not "chart."
- width? · height? · area?
- Rendered size in px (120×32 default) and the soft area fill.
- tone?: VizTone = 'default'
- Accent for the set-point dot (and the official-brand line).
ProgressArc
- value · min? · max?
- The reading and its scale; clamped, defaults 0–100.
- kind?: 'progress' | 'score'
- progress renders role="progressbar"; score renders role="meter."
- label · hideLabel?
- Visible label under the ring (the accessible name); hideLabel keeps the name but hides the text.
- tone? · reading? · unit? · size?
- Same accent, formatted-reading, and sizing contract as Gauge; 72px default.
Usage
import { Gauge } from '@halo-compliance/ui'
<Gauge
value={readiness}
label="Rulebook readiness"
unit="%"
/>
// Mount performs the calibration settle; value updates glide.
// Reduced motion renders the needle at rest on the value.import { SeverityBars } from '@halo-compliance/ui'
<SeverityBars
label="Findings by severity"
items={[
{ tone: 'critical', label: 'Critical', value: 3 },
{ tone: 'caution', label: 'Caution', value: 11 },
{ tone: 'affirm', label: 'Affirmed', value: 38 },
]}
/>
// Each row pairs glyph + text label + color (ADR-0017) —
// no extra wiring needed to stay accessible.Anti-patterns
- Don't gauge a count. A gauge needs a bounded scale (a percentage, a score against a maximum). "127 controls" has no ceiling — print the number, or give it a Sparkline for trend.
- Don't let the geometry carry the value alone. The arc, the bar, and the line are supplementary; the printed number is the record. If a design hides the number to look cleaner, it hides the data.
- Don't recolor severity by hand. Tones come from the shared viz register, which pairs the glyph and lifts accents for dark mode. An inline stroke override silently drops both.
- Don't build a dashboard out of these. These are inline charts — values placed where a decision happens. A wall of arcs is status theater; if a page is becoming one, it needs a table.