Here's an uncomfortable stat: most color palettes that look great fail the most basic accessibility test — text contrast.
I learned this the hard way. I built a landing page with a palette I loved: soft coral text on a warm cream background. Beautiful in a screenshot. Then someone told me they literally couldn't read my hero heading on their phone in daylight.
The palette wasn't the problem. The pairing was.
The 30-second check
Text readability is measured by contrast ratio — how different your text color is from the background behind it. The WCAG standard is simple:
- 4.5:1 minimum for normal body text (AA)
- 7:1 for the stricter AAA level
- 3:1 for large text (24px+)
Black on white is 21:1 — maximum contrast. My coral-on-cream disaster? About 2.1:1. Nobody with less-than-perfect vision (or a sunny day) had a chance.
The fastest way to check: any contrast checker tool, or the one built into Chrome DevTools — inspect a text element, click the color swatch in Styles, and the contrast ratio is right there with AA/AAA badges.
The formula, if you're curious
The math behind it is relative luminance:
function luminance(r, g, b) {
const a = [r, g, b].map(v => {
v /= 255;
return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
});
return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
}
function contrast(rgb1, rgb2) {
const l1 = luminance(...rgb1);
const l2 = luminance(...rgb2);
return (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05);
}
contrast([255, 255, 255], [17, 17, 17]); // ≈ 18.88
That's the exact formula Chrome, Lighthouse, and every checker uses. No magic.
The workflow fix
The real lesson wasn't "run a checker at the end" — it was choosing pairings up front. Now when I pick a palette, I decide immediately: which color is text, which is background, which are decoration-only. The vivid middle tones that fail contrast? They become borders, accents, and illustrations — never words.
A pattern that always works:
:root {
--bg: /* your lightest color */;
--text: /* your darkest color */;
--accent: /* the vivid one — buttons, badges, never body text */;
}
If your lightest-on-darkest doesn't hit 4.5:1, the palette needs a darker dark or a lighter light. Adjust lightness, not hue — the palette keeps its character.
I ended up building this check into PaletteCSS, a free palette tool I work on — every palette page shows a WCAG contrast table for all color pairings, so you can see which combos are safe for text before you commit. No signup, if you want to sanity-check your current palette.
TL;DR
- Body text needs 4.5:1 contrast minimum
- Check in DevTools — it's built in
- Assign roles when you pick colors: text, background, accent
- Vivid mid-tones are for decoration, not words
Your design isn't done when it looks good. It's done when everyone can read it.
Top comments (0)