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.

Live readiness meter
Set a new reading to watch the arc glide; replay the mount to watch it draw up.

Severity tones remap the accent and pair the ADR-0017 glyph beside the reading — a severity is never color alone.

Severity-toned gauges
The glyph rides the reading, not the dial — strip the hue and the tone still reads.

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.

Findings by severity
  • Critical3
  • Caution11
  • Affirmed38
  • Informational6
Bars are proportional to the largest severity; the counts are the data.

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.

Inline, in a sentence

Open findings are trending down from 14 in March to 3 this week.

The sparkline sits in the text baseline like a word; the sentence carries the numbers.
With area fill
The soft fill weights the trend without adding a second color.

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."

Progress and score
Same ring, two semantics — and the severity tones pair the glyph in the center.

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

Readiness gauge with live updatestsx
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.
Severity distribution — ADR-0017 pairing built intsx
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.
  • /icons — the ADR-0017 severity glyph set these primitives pair.
  • /motion — the calibration-settle motion register the needle reuses.
  • /loading — the mark-as-loader; the same dial, indeterminate.
  • /badge — severity tones on text-scale status.