Skip to content
Workshop tools
All tools

Theme and contrast lab

Edit a handful of design tokens and get light, dark and high-contrast themes with live WCAG contrast ratios, a colour-blind check and CSS you can export.

Runs in your browser. Nothing is uploaded.

Tool

Theme

Light tokens

OKLCH sliders
OKLCH sliders
OKLCH sliders
OKLCH sliders
OKLCH sliders
OKLCH sliders
OKLCH sliders
OKLCH sliders
OKLCH sliders

Preview

A sample card, button, link, input, alerts and chart in the selected theme. It is for visual review only and is not interactive. The colour-vision simulation affects this preview only.

Order summary

Two items, delivered in three days.

View delivery details

Place orderPromo codeFocus ring

Error: the card number is not valid.

Success: your address was saved.

Bar chart with three bars: accent, danger and success, each labelled.AccentDangerSuccess

Contrast matrix: Light

Every pair meets its target. Ratios are truncated, never rounded up. This reports ratios and thresholds; it does not say a theme is compliant.

WCAG contrast ratio for each token pair, with pass or fail per level and an advisory APCA value.
PairRatioAA text (4.5:1)AA large text (3:1)UI components (3:1)AAA text (7:1)APCA (advisory)Fix
text on bg#18181b on #f8f8f5; target 4.5:116.65:1PassPassPassPassLc 100Meets target
text on surface#18181b on #ffffff; target 4.5:117.71:1PassPassPassPassLc 105Meets target
text-muted on bg#52525b on #f8f8f5; target 4.5:17.26:1PassPassPassPassLc 83Meets target
text-muted on surface#52525b on #ffffff; target 4.5:17.72:1PassPassPassPassLc 87Meets target
accent on bg#1d4ed8 on #f8f8f5; target 4.5:16.29:1PassPassPassFailLc 78Meets target
danger on bg#b42318 on #f8f8f5; target 4.5:16.17:1PassPassPassFailLc 77Meets target
success on bg#166534 on #f8f8f5; target 4.5:16.70:1PassPassPassFailLc 80Meets target
border on bg#71717a on #f8f8f5; target 3:14.54:1PassPassPassFailLc 69Meets target
focus on bg#1d4ed8 on #f8f8f5; target 3:16.29:1PassPassPassFailLc 78Meets target

Colour-blind check

Do the state colours stay apart under each simulation? A pair counts as distinct when its OKLab distance is at least 0.1. That threshold is this tool's own heuristic, not a standard. State must also never rely on colour alone: use icons and text as well.

Distance between pairs of state colours under each colour vision simulation.
PairNormal visionProtanopiaDeuteranopiaTritanopiaAchromatopsia
danger and successDistinct (0.26)Hard to tell (0.07)Hard to tell (0.09)Distinct (0.30)Hard to tell (0.02)
danger and accentDistinct (0.35)Distinct (0.30)Distinct (0.31)Distinct (0.30)Hard to tell (0.00)
success and accentDistinct (0.28)Distinct (0.27)Distinct (0.26)Hard to tell (0.09)Hard to tell (0.01)

Export

:root {
  --color-bg: #f8f8f5;
  --color-surface: #ffffff;
  --color-text: #18181b;
  --color-text-muted: #52525b;
  --color-accent: #1d4ed8;
  --color-border: #71717a;
  --color-focus: #1d4ed8;
  --color-danger: #b42318;
  --color-success: #166534;
}

@media (prefers-color-scheme: dark) {
  :root {
    --color-bg: #121315;
    --color-surface: #1b1c1f;
    --color-text: #ececee;
    --color-text-muted: #a1a1aa;
    --color-accent: #8ab4ff;
    --color-border: #8b8b93;
    --color-focus: #8ab4ff;
    --color-danger: #ff8f85;
    --color-success: #6fd48a;
  }
}

@media (prefers-contrast: more) {
  :root {
    --color-bg: #ffffff;
    --color-surface: #ffffff;
    --color-text: #000000;
    --color-text-muted: #52525b;
    --color-accent: #1a4ad4;
    --color-border: #71717a;
    --color-focus: #1d4ed8;
    --color-danger: #af1c12;
    --color-success: #166534;
  }
}

The high-contrast values are derived from the Light tokens and exported under prefers-contrast: more. The forced-colors block hands control to the user's own system colours.

Import

Names such as --color-bg and --color-text-muted, with hex values. Values inside a prefers-color-scheme: dark block, or the second value of light-dark(), set the dark theme.

How it works

Pick Light or Dark and edit the nine tokens, by hex or with OKLCH sliders. The matrix shows the WCAG 2 contrast ratio for each pair, with pass or fail for AA text (4.5:1), AA large text (3:1), UI components (3:1) and AAA text (7:1). Ratios use the relative luminance definition in the current WCAG 2.2 text, with the 0.04045 threshold.

When a pair misses its target, auto-fix proposes the nearest colour that passes by changing OKLCH lightness while keeping hue and chroma where the sRGB gamut allows. If no colour can pass on that background, it says so.

The high-contrast theme is derived from the Light or Dark tokens. The tokens, and nothing else, are stored in the URL hash, so a link restores the same lab.

Colour-vision simulations use the Machado, Oliveira and Fernandes (2009) matrices at severity 1.0, applied in linear RGB through SVG filters on the preview only. Achromatopsia is luminance only.

Keyboard and text equivalent

Everything works from the keyboard: Tab through the controls, arrow keys move the theme choice and the sliders, and Enter or Space press buttons. Every colour has a hex field, so the native colour picker is optional. The preview shows a card, a button, a link, an input, an error and a success message, a focus ring and a three-bar chart in the selected theme; the matrix lists the same information as text.

What this can't check
  • It reports ratios and thresholds. It does not say a theme is compliant: compliance involves more than colour.
  • It checks the token pairs listed in the matrix, not every combination your design uses, and not text over images or gradients.
  • The colour-vision simulations are approximations of dichromacy at full severity. They are not a clinical or user-research result.
  • The colour-blind check uses an OKLab distance threshold chosen for this tool, not a standard.
  • APCA values are advisory. APCA is part of a WCAG 3 draft and is not a WCAG 2 conformance requirement.
  • The high-contrast theme is derived from your tokens with this tool's own targets (7:1 for text, 4.5:1 for UI). Users in forced-colors mode get their own system colours.

References

See more of my work