Tools

Accessibility check

WCAG 2.1 contrast audit of every token pair in the system, computed from the actual CSS values currently loaded. It's user-led — press Run to audit the current implementation; re-run after a theme toggle or token edit to refresh.

Not run yet — press Run to test the live tokens.
Theme tested
Light
Switch with the moon icon in the sidebar.
Pairs tested
—
Text/surface combinations across the system.
AA passing
—
Each pair tested against its intended use.
Issues
—
Pairs failing at their use level.
AAA ≥ 7.0 (normal) / ≥ 4.5 (large)
AA ≥ 4.5 (normal) / ≥ 3.0 (large & UI)
UI ≥ 3.0 — non-text UI only
Fail < 3.0
Show
Test against

Issues this tool surfaced

Running the checker against v1.1 caught five real problems I'd missed in the manual audit. Three were fixed on the spot; two needed a broader hue review and were deferred — both since resolved: F-015 when the accent consolidated to cobalt (v1.3), F-014 when brand moved off green (v1.5).

Fixed
F-013 · Helper text failed AA on muted surfaces

In light mode, --text-tertiary on --surface-muted scored 4.22:1 — under the 4.5:1 AA threshold for body copy. The earlier audit nudged tertiary once already (v1.1); this checker caught that it wasn't enough. Darkened again to #6E625F, now 4.95:1.

Fixed
F-014 · Brand text on white fails AA

The forest green --brand (#629460) on white was 3.54:1 — fine for dots and focus rings, a fail as text. Deferred since v1.1. Resolved v1.5: brand moved off green to cobalt (#2F4BA6, 8.3:1 on white), closing the brand-on-white fail. The green-as-text concern transferred to the new --success token (same ~3.5:1 physics) and is now scoped there: --success-dark for any text role, bare --success for non-text only. A lint rule against bare --success on text is queued.

Fixed
F-015 · Accent button label invisible

White text on the old sky-blue --accent (#0EA5E9) scored 2.77:1 — failed AA and the 3:1 UI threshold. Resolved v1.3: the palette consolidated to a single cobalt accent — --accent is now #2F4BA6 (white on it = 8.3:1, passes both); the sky blue is retained chart-only as --azure-500.

Boundary
F-016 · Strong border fails 3:1 UI threshold

--border-strong on white scores 1.88:1 — under the 3:1 WCAG 1.4.11 threshold for input borders. Darkening to pass would harm the system's hand-drawn warmth. Documented compromise: every input pairs the border with a label outside and a focus shadow ring; the boundary indicator is the multi-cue combination, not the border alone. WCAG considers a non-color signal sufficient.

Boundary
F-017 · Disabled label below 3:1

Disabled text scores 2.24 (card) / 1.93 (muted) in light, 2.90 in dark. WCAG explicitly exempts inactive UI from contrast requirements (SC 1.4.3 / 1.4.11 informative note) — disabled controls should look disabled. The checker flags them for transparency, but no fix is owed. The disabled opacity rule (0.45) combines with this for a stronger visual cue.

How this works

No fixtures, no hardcoded values — every contrast ratio on this page is computed from the live :root custom properties at render time.

Token lookup

The script reads each pair as getComputedStyle(document.documentElement).getPropertyValue('--text-primary'), parses the hex (or rgb) into RGB triplets, and stores both sides.

Relative luminance

Each channel is linearised per WCAG 2.1: divide by 255, then either c/12.92 if c ≤ 0.03928 or ((c+0.055)/1.055)2.4. Luminance L = 0.2126·R + 0.7152·G + 0.0722·B.

Contrast ratio

Ratio = (Llighter + 0.05) / (Ldarker + 0.05). A pair scores up to 21:1 (pure black on pure white). The page reports to one decimal place, matching most external auditors.

Pass/fail logic

Each pair is tagged with its intended use: body text, UI element, or large heading. Thresholds applied per-use — a brand swatch on white isn't held to body-text 4.5:1 because it never carries body text.

What this check doesn't cover

Contrast is one of many a11y axes. Honesty about scope is part of the system.

Keyboard navigation

Focus order, focus traps, and skip links need real DOM walks — covered manually in the v1.1 audit (F-007, F-012).

Screen reader semantics

ARIA roles, live regions, and label associations need testing with NVDA / VoiceOver. Deferred to v1.2 (F-011).

Colour-blind simulation

Contrast alone doesn't catch deuteranopia / protanopia confusion (e.g. red/green chips). The system intentionally uses non-colour signals — icons, text labels — in every status component.

Hit-target sizing

WCAG 2.2 AAA wants ≥ 44×44px targets. Tiny Wire is desktop-dense at 32px controls — fails for touch-primary use, intentional for operational tools. Documented as a known boundary.