S
Shadl

Hit space. Get colors that actually pass.

Five swatches. Locked or fresh. Spacebar to regenerate, click the lock to keep one. The URL is the database, so a paste into Slack is a full save.

Re-rolls every unlocked swatch. Locks stay put.
Through other eyes, non-destructive

Pair preview: 14 · 18.66 · UI

text on bgThe quick brown fox jumps over the lazy dog.Headlines breathe here.Continue
text on surfaceThe quick brown fox jumps over the lazy dog.Headlines breathe here.Continue
primary on bgThe quick brown fox jumps over the lazy dog.Headlines breathe here.Continue
primary on surfaceThe quick brown fox jumps over the lazy dog.Headlines breathe here.Continue
accent on bgThe quick brown fox jumps over the lazy dog.Headlines breathe here.Continue
accent on surfaceThe quick brown fox jumps over the lazy dog.Headlines breathe here.Continue
UI sample
Quiet alarms, loud signal.

A live preview of this palette on something real: heading, body, an inline link, a button, a chip.

v4.2 · beta

Normal text needs a 4.5:1 contrast ratio to clear WCAG AA, large text only 3:1, and APCA scores the same pair differently depending on which color sits on top. Those thresholds, plus the data they rest on, are collected here: the contrast minimum each standard sets by context, the APCA Lc readability levels, color vision deficiency prevalence, the OKLCH channel ranges the generator draws from, and the exact syntax of every token format the tool exports.

WCAG 2.1 contrast minimums by context

WCAG sets a different minimum ratio depending on text size, text weight, and whether the element is text or a non-text control.

Element / contextAA minimumAAA minimumSuccess Criterion
Normal text (below 24px, or below 18.66px bold)4.5:17:11.4.3 / 1.4.6
Large text (24px+, or 18.66px+ bold)3:14.5:11.4.3 / 1.4.6
UI components and graphical objects3:1n/a1.4.11
Disabled controls, pure decoration, logotypesnonenoneexempt

Large text is 18pt (24px) at a regular weight, or 14pt (18.66px) at bold. The ratio always divides the brighter relative luminance by the darker, so it reads the same regardless of which color is the foreground.

APCA Lc levels and intended use

APCA reports a polarity-signed lightness contrast (Lc) that runs from about -108 to 106; Shadl displays the absolute value. A higher Lc clears smaller and thinner text.

Lc (abs)ClearsTypical use
90Body text at the smallest and thinnest sizesPreferred floor for fluent reading
75Body text and columns of running copyStandard body-text target
60Larger fluent text and non-body contentSub-headings, larger paragraphs
45Large or bold headline text, large UI shapesDisplay type, large buttons
30Text that only needs to be noticedPlaceholders, disabled labels, copyright
15Non-text elements that carry meaningBorders, focus rings, icon strokes

These are readability levels, not strict size cutoffs. The full size-by-weight matrix (pixel size against font weights 100 to 900) lives in the APCA documentation linked under Related concepts.

Color vision deficiency types and prevalence

About 1 in 12 men and 1 in 200 women of Northern-European ancestry have some color vision deficiency. Shadl simulates the three dichromatic types side by side.

TypeCone affectedPrevalence (men)Colors most often confused
Deuteranomaly (green-weak)M (green), shifted~4.6%green/red, green/brown
Deuteranopia (green-blind)M (green), absent~1.2%green/red, blue/purple
Protanomaly (red-weak)L (red), shifted~1.3%red/green; reds look dim
Protanopia (red-blind)L (red), absent~1.0%red/green/brown; reds darken
Tritan (blue-yellow)S (blue)~0.01% (sexes equal)blue/green, yellow/violet
Achromatopsia (monochromacy)all cones / rods only~0.003%all hues

Red-green deficiencies (deutan plus protan) account for roughly 99% of cases. That is why a palette leaning on red-versus-green to signal state is the single most common accessibility trap.

OKLCH channels

Shadl draws unlocked swatches from OKLCH and reports each swatch in it. The three channels behave as follows.

ChannelRangeWhat it controlsNotes
L (lightness)0 to 1 (0% to 100%)Perceptual brightness; 0 is black, 1 is whiteEqual L steps look equally large across hues
C (chroma)0 to ~0.37 in sRGBColorfulness; 0 is neutral grayMaximum in-gamut chroma depends on L and H
H (hue)0° to 360°Hue angle~30 red, ~90 yellow, ~145 green, ~195 cyan, ~265 blue, ~330 magenta

CSS syntax is oklch(L C H), for example oklch(0.52 0.2 27). Not every L/C/H triple is displayable in sRGB; high chroma near black or white clips to the gamut edge.

Export formats for design tokens

Shadl writes the same five swatches (bg, surface, text, primary, accent) into four token formats. Copy whichever matches the target codebase. The snippets below use the default starter palette; the tool emits exact OKLCH conversions for the colors on screen.

FormatColor modelDrop-in target
CSS custom propertieshex and oklchAny stylesheet, inside a :root block
Tailwind v4 @themeoklchglobals.css in a Tailwind v4 project
Tailwind v3 confighextheme.extend.colors in tailwind.config.js
JSON tokenshexDesign-token pipelines such as Style Dictionary
CSS custom properties
:root {
  --bg: #f4eedd;
  --surface: #e6dbbe;
  --text: #1a0e10;
  --primary: #c9302a;
  --accent: #2a5cff;
}
Tailwind v4 · globals.css
@theme {
  --color-bg: oklch(0.94 0.02 90);
  --color-surface: oklch(0.88 0.04 88);
  --color-text: oklch(0.20 0.02 18);
  --color-primary: oklch(0.52 0.20 27);
  --color-accent: oklch(0.55 0.24 264);
}
Tailwind v3 · tailwind.config.js
module.exports = {
  theme: {
    extend: {
      colors: {
        bg: "#f4eedd",
        surface: "#e6dbbe",
        text: "#1a0e10",
        primary: "#c9302a",
        accent: "#2a5cff",
      },
    },
  },
};
JSON tokens
{
  "bg": "#f4eedd",
  "surface": "#e6dbbe",
  "text": "#1a0e10",
  "primary": "#c9302a",
  "accent": "#2a5cff"
}

WCAG contrast: a worked example

The ratio is the brighter relative luminance plus 0.05, divided by the darker plus 0.05. Worked here for gray #767676 text on white #FFFFFF, the lightest gray that still clears AA on white.

StepWhite #FFFFFFGray #767676
sRGB channel (0 to 1)1.0000.463
Linearized channel1.0000.181
Relative luminance L1.0000.181

Linearization applies ((c + 0.055) / 1.055) ^ 2.4 to any channel above 0.03928, then luminance weights the channels 0.2126 R, 0.7152 G, 0.0722 B. The result:

ratio
ratio = (1.000 + 0.05) / (0.181 + 0.05)
      = 1.05 / 0.231
      = 4.54:1

That clears the 4.5:1 floor for normal text by a hair, which is exactly why #767676 is the canonical “lightest passing gray” reference value.

Standards and regulations that cite these numbers

Where the thresholds come from and who enforces them.

StandardStatus / yearContrast basisScope
WCAG 2.0W3C Recommendation, 20084.5:1 (SC 1.4.3)Foundational web standard
WCAG 2.1W3C Recommendation, 2018adds 3:1 non-text (SC 1.4.11)Current baseline most audits cite
WCAG 2.2W3C Recommendation, 2023no new contrast SCAdds focus and target-size criteria
WCAG 3.0Working Draft, ongoingAPCA (perceptual)Not normative; do not cite for compliance
EN 301 549EU harmonized standardWCAG 2.1 AAEU public sector and EAA reference
EAA, Directive (EU) 2019/882Enforced 28 Jun 2025WCAG 2.1 AA via EN 301 549Private-sector products and services in the EU
Section 508 (US)Revised rule, 2017WCAG 2.0 AAUS federal procurement
Read the full methodology on /learn