Lumen
Foundations

Accessibility

A floor we measure, never a matter of taste.

Contrast is computed (WCAG and APCA), motion is optional, and meaning never rests on color alone.

Contrast (computed)

Body text ≥ 4.5:1, UI and large text ≥ 3:1, in both Day and Night. Colored text uses a darker -text variant; the bright token is reserved for fills and dots. The /design-system-review skill's contrast.py audits every WCAG pairing; lumen/system/scripts/apca.py (self-tested against the apca-w3 reference values) covers the saturated mid-tones WCAG underrates.

APCA on the saturated fills (computed 2026-06-09):

Label on fillWCAGAPCA LcRead
white on --primary #EA580C (gradient start)3.5667.6✅ bold UI labels
white on the gradient end (color-mix ≈ #EA640A)3.3265.2✅ near-uniform with the start
white on --glow #E89200 (the old gradient end — retired)2.46 ❌52.9 ❌why the gradient was fixed
white on --destructive #DC26264.8377.0
#1A0E04 on Night --primary #FF8A338.0657.5✅ WCAG-strong; APCA-borderline for small labels — keep Night button labels ≥ 14px semibold

Bright is for fills, not text

--primary (#EA580C) is a fill/dot color. For small colored text use --primary-text (#A8430A), which clears AA on the light ground. Every saturated hue ships a matching -text step — including --data-4-text. Never use --glow under a light text label.

Beyond color

Status never depends on hue alone — it pairs color with an icon and a label, so it survives the grayscale test and works for color-vision deficiencies.

StateColorIconSurvives grayscale?
On tracksuccesscheck
Watchwarningtriangle
Criticaldangeroctagon

Motion

Every animation respects prefers-reduced-motion. Energy is earned — tied to a real signal — never ambient noise. Under reduced-motion, transitions collapse to near-instant and looping animation stops.

On this page