Most palettes are built in the wrong order: pick five colors that look great together, ship them, then discover in an audit that half the combinations are unreadable. Accessibility isn’t a paint job you apply at the end — it’s a set of constraints that are nearly free if you start with them. Here’s the order of operations that works.
1. Lock the text colors first
Before any brand color: choose your page background, your body text color, and your secondary (muted) text color. Body text should clear 7:1 (AAA — cheap here, worth it), and muted text must still clear 4.5:1. The most common palette failure on the web is a muted gray that dips to 3.8:1 because it looked “calmer.”
#1A1A1Atext#FFFFFFbackground#6B6B6Btext#FFFFFFbackground2. Derive the brand and accent colors
Now bring in the brand color — as a role, not a hex. Decide where it appears: filled buttons (brand background + white or black text), links and interactive text (brand-as-text on the page background), and decorative moments (no constraint). Each role gets its own variant, tuned in OKLCH so hue never drifts — same hue, adjusted lightness. For picking companions to the brand hue — an accent, a secondary — harmony schemes at constant perceptual lightness do the heavy lifting; that’s exactly what the palette generator produces, with a contrast score on every swatch.
3. Give every semantic color a working pair
Success green, warning amber, danger red: each needs to function two ways —
- as a UI element (badge fill, border, icon): 3:1 against the surface it sits on;
- as text (error messages, inline status): 4.5:1 against the page background.
Amber is the classic casualty — a yellow-orange that reads as “warning” is rarely 4.5:1 on white. The standard answer is two tokens per semantic color: a vivid fill (with dark text on it) and a darkened text variant.
4. Never let color carry meaning alone
Roughly 1 in 12 men has some form of color-vision deficiency; your red/green status dots are identical to them. The palette isn’t finished until every color-coded state also differs by shape, icon, label, or position. This costs nothing at design time and is expensive to retrofit.
5. Test pairs, not swatches
A palette isn’t five colors — it’s every combination you’ll actually render. Enumerate them (text on background, text on brand, badge on card, icon on muted surface…) and check each one. It’s minutes of work with a contrast checker — or one call to a batch contrast API if your palette lives in code. Save the passing pairs somewhere findable; on PaletteSense the heart icon keeps them one click away.
The checklist
- Background + body text (7:1) + muted text (4.5:1) locked first
- Brand color split into roles: fill, text, decoration
- Semantic colors: 3:1 as UI, 4.5:1 as text — two tokens if needed
- No meaning carried by color alone
- Every real pair tested, not just the swatches
Follow that order and the audit becomes a formality — the palette was born compliant.
Try it in the tool
Start from any base color: generate a scored palette, browse 31,000+ named colors with their contrast data, or check any pair in the contrast checker.