Contrast that passes and still looks good
WCAG contrast ratios are a floor, not a style. What the numbers measure, where designs usually fail them, and how to build a palette that clears them without looking harsh.

Accessible contrast has a reputation for producing ugly palettes: black on white, a saturated blue for links, no subtlety anywhere. In practice the requirements are narrower than that reputation, and most of the failures I see in reviews come from three or four habits that are easy to change.
What the ratio measures
WCAG 2 compares the relative luminance of two colours. Luminance is a weighted sum of the red, green and blue channels after removing the sRGB gamma curve, with green weighted most heavily because our eyes are most sensitive to it. The ratio is (L1 + 0.05) / (L2 + 0.05), where L1 is the lighter colour, and it runs from 1:1 (identical) to 21:1 (black on white).
The thresholds for level AA:
- 4.5:1 for normal text.
- 3:1 for large text, which WCAG defines as at least 24px regular, or about 18.7px bold.
- 3:1 for the parts of controls and graphics people need to see to use them: input borders, focus indicators, icon buttons, chart lines that carry meaning.
Disabled controls, logos and purely decorative elements are exempt.
Where designs usually fail
Grey secondary text. This is the most common failure by far. A designer sets captions or metadata in a light grey because it looks refined at full screen brightness on a good monitor. #999 on white is 2.85:1. On a phone outdoors it disappears. Body-adjacent text needs to clear 4.5:1 even when it is meant to be quiet; #767676 is about the lightest grey that does on pure white.
Placeholder text. Browsers default it to a light grey, and it often carries the only hint of what a field expects. Either give it real contrast or, better, move the hint into a visible label or description.
Input borders. A field outlined in #e0e0e0 on white has no visible boundary for many people. The border, or whatever else marks the field, needs 3:1 against the background.
Text on images. A headline that passes over the dark part of a photograph and fails over the light part fails. Put a solid or semi-opaque scrim under it, or move the text off the picture.
States. The default button passes; the hover colour, chosen later, does not. Check every state you design, including focus.
Making a palette that clears the bar
I work from the neutrals out. Pick the background first, then find the ink that gives you around 12:1 to 15:1 against it. That is plenty, and it avoids the glare of pure black on pure white, which some readers, especially those with dyslexia or light sensitivity, find tiring. A warm off-white with a very dark warm grey reads softer than #fff on #000 and still clears AAA.
Then set a secondary text colour as the lightest value that still clears 4.5:1, and stop. You get two levels of emphasis that both pass, and the hierarchy comes from size and weight more than from fading.
:root {
--paper: #efebe3;
--ink: #1f1a17; /* 14.5:1 on paper */
--muted: #5b524b; /* 6.4:1 on paper */
--accent: #7b1e2b; /* 8.6:1 on paper, usable for text and links */
}
For the accent, test it as text, not as a swatch. A colour that looks rich at 200 by 200 pixels may be thin and hard to read as a 16px link. If the brand colour fails as text, use it for fills and large shapes and derive a darker shade for links.
Colour is never the only signal
WCAG also requires that colour alone not carry meaning. Links in running text need an underline or some other non-colour cue, unless their colour contrasts 3:1 with the surrounding text and they gain a cue on hover and focus. Error fields need an icon or a message, not just a red border. A chart needs labels or patterns as well as hues.
This is less of a constraint than it sounds. Underlined links are easier to spot for everyone, and a short error message is more useful than a colour.
Dark themes
A dark theme is not the light theme inverted. Pure white text on pure black is 21:1, and at that contrast thin strokes appear to glow and spread, which is uncomfortable to read for long. Lift the background to a very dark grey, bring the text down to an off-white, and use a slightly lighter weight for body text than you would on paper. Accents usually need to be lighter and less saturated than their light-theme versions to reach the same ratio without vibrating.
About APCA
You may see the APCA method mentioned as the future of contrast. It models perception better, especially for dark themes and for thin type, and it is being developed for a future version of WCAG. It is not yet the standard that audits and regulations test against. I check new palettes with both, and when they disagree I take the stricter result.
A routine that works
Keep the ratios next to the tokens, as comments or in the design file. Check every new pairing when it is introduced rather than in an audit at the end. And look at the result on a cheap phone in daylight, at the brightness people actually use. Contrast is a measurement of colours; readability is what happens when someone tries to use them.