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-10-23, the v2 values):

Label on fillWCAGAPCA LcRead
white on --primary #241917 (the action, in ink; gradient start)17.12106.3✅ body-size labels
white on the gradient end (color-mix ≈ #412B12)13.30102.8✅
white on --brand #EA580C (Solar, the mark)3.5667.6⚠️ Solar is Halo's mark, never a label's ground
white on --destructive #D42A2A5.0478.5✅
#18120F on Night --primary #EFE8DF15.2692.0✅

Edges (WCAG 1.4.11, 3:1 for the edge that identifies a control):

EdgeOnWCAGRead
--border-strong #968476 (= --input)Day card #FFFDFA3.53✅ every control's edge
--border-strong #857467Night card #221A163.82✅
--border #E6DCD0Day card1.33decorative only: dividers inside a card, never a control

lumen/system/scripts/check_truth.py enforces both tables: the action label at 4.5:1, every control edge at 3:1 on each surface a control can sit on, and every brand, status and data mark at 3:1 on a card.

Bright is for fills, not text

--brand (#EA580C) is a fill/dot color. For small colored text use --primary-text (#A8430A), which clears AA on the light ground, the card and the muted fill. 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