The contrast matrix
Of the 64,262 ordered pairs you can make from the 254 colors in this library, 8,930 clear WCAG AA for normal text. That is 13.9 percent. Pick a background below to see exactly which ones, at which ratio. The same explorer covers the Tailwind CSS 4.3.1 palette, with every pair as a CSV.
What this tells you
The number that surprises most people is how low 13.9 percent is. Two colors picked at random from a curated, tasteful palette clear AA for body text less than half the time. Contrast is a function of luminance, and most palettes cluster their colors in the middle of the luminance range, where nothing contrasts with anything.
That is also why a palette needs a light end and a dark end before it needs more hues. If every color in a set sits between 40 and 60 percent lightness, no amount of hue variety will make the set accessible.
The five hardest backgrounds in this library
These have the fewest library colors that pass AA as text on them.
| Background | Colors passing AA | Best available | On white |
|---|---|---|---|
| Burnt Orange | 1 of 253 | Black, 4.87:1 | 4.31:1 |
| Forest Green | 1 of 253 | Black, 4.78:1 | 4.39:1 |
| Cerise | 1 of 253 | Black, 4.73:1 | 4.44:1 |
| Light Brown | 1 of 253 | Black, 4.84:1 | 4.34:1 |
| Red | 2 of 253 | Black, 5.25:1 | 4.00:1 |
The five most forgiving backgrounds
| Background | Colors passing AA | Best available | On ink black |
|---|---|---|---|
| Black | 176 of 253 | White, 21.00:1 | 1.10:1 |
| Ink Black | 170 of 253 | White, 19.14:1 | 1.00:1 |
| Eerie Black | 152 of 253 | White, 17.22:1 | 1.11:1 |
| Navy | 143 of 253 | White, 16.01:1 | 1.20:1 |
| Dark Blue | 136 of 253 | White, 15.30:1 | 1.25:1 |
Tailwind CSS 4.3.1 contrast pairs
Of the 40,755 unique pairs that can be made from the 286 color tokens in Tailwind CSS 4.3.1, 13,581 (33.3 percent) clear WCAG AA for normal text at 4.5:1, 19,034 (46.7 percent) clear AA for large text at 3:1, and 8,146 (20.0 percent) clear AAA at 7:1.
Each pair is counted once, because contrast is symmetric, and a token is never paired with itself. Different tokens that render to the same hex stay in, at 1.00:1: there are 4 such pairs, zinc-50 and neutral-50 among them. The set is 26 color families of 11 shades each; black and white are not tokens in this list, so they are not in the pairs either.
The hex values are HueKit's sRGB rendering of the OKLCH values Tailwind publishes, clamped into sRGB where a token sits outside it, so every ratio describes what an sRGB screen shows. Pass and fail use the unrounded ratio, as WCAG intends, while the CSV rounds the ratio to two decimals for reading. That gap matters for 54 pairs, which show a rounded ratio at a threshold but still fail it.
Columns: fg_token, fg_hex, bg_token, bg_hex, ratio, aa_normal, aa_large, aaa. Built in your browser from the token data already on this page, so nothing is fetched.
Which shade to use for text, per family
For each family, the lightest shade that clears AA as normal text on white (#FFFFFF) and the darkest shade that clears it on black (#000000).
| Family | On white, lightest AA shade | On black, darkest AA shade |
|---|---|---|
| red | red-600, 4.77:1 | red-500, 5.52:1 |
| orange | orange-700, 5.22:1 | orange-600, 5.84:1 |
| amber | amber-700, 5.03:1 | amber-600, 6.56:1 |
| yellow | yellow-700, 4.93:1 | yellow-600, 7.15:1 |
| lime | lime-700, 4.98:1 | lime-600, 6.86:1 |
| green | green-700, 4.95:1 | green-600, 6.52:1 |
| emerald | emerald-700, 5.36:1 | emerald-600, 5.75:1 |
| teal | teal-700, 5.36:1 | teal-600, 5.72:1 |
| cyan | cyan-700, 5.28:1 | cyan-600, 5.80:1 |
| sky | sky-700, 5.86:1 | sky-600, 5.22:1 |
| blue | blue-600, 5.25:1 | blue-500, 5.58:1 |
| indigo | indigo-500, 4.58:1 | indigo-500, 4.59:1 |
| violet | violet-600, 5.89:1 | violet-500, 4.77:1 |
| purple | purple-600, 5.54:1 | purple-500, 5.10:1 |
| fuchsia | fuchsia-600, 4.66:1 | fuchsia-600, 4.51:1 |
| pink | pink-600, 4.54:1 | pink-600, 4.63:1 |
| rose | rose-600, 4.53:1 | rose-600, 4.64:1 |
| slate | slate-500, 4.76:1 | slate-400, 7.98:1 |
| gray | gray-500, 4.84:1 | gray-400, 8.07:1 |
| zinc | zinc-500, 4.83:1 | zinc-400, 8.01:1 |
| neutral | neutral-500, 4.74:1 | neutral-400, 8.13:1 |
| stone | stone-500, 4.79:1 | stone-400, 8.12:1 |
| mauve | mauve-500, 5.10:1 | mauve-400, 8.13:1 |
| olive | olive-600, 6.90:1 | olive-500, 4.94:1 |
| mist | mist-500, 4.61:1 | mist-500, 4.55:1 |
| taupe | taupe-500, 4.95:1 | taupe-400, 8.24:1 |
Building your own set instead? The palette generator checks every pair of a generated palette against the same thresholds.
How it is calculated, and its limits
Every ratio uses the WCAG relative luminance formula, unchanged between WCAG 2.1 and 2.2. It is a luminance calculation, not a perceptual model: it says nothing about whether two colors of similar luminance are distinguishable, and nothing about how a combination reads for someone with a color vision deficiency. A pair can clear 7:1 and still be a poor choice. See the methodology for the full formula and what it does not model.
No pair gets its own page. Generating 64,262 near-identical URLs is the fastest way to teach a search engine that a site is not worth crawling, and the data is more useful filterable than paginated.
Related
Last updated by FusionStudios. Every value on this page is computed by the same functions the tools use. How this is calculated.