WCAG Contrast Ratios Explained: Where 4.5:1 Actually Comes From
What the WCAG 2.1 contrast thresholds mean, how the ratio is calculated from relative luminance, when 3:1 is enough, and the common mistakes that fail an audit.
Most teams learn one number — 4.5:1 — and stop there. That gets you through a checklist but not through an audit, because the threshold that applies depends on text size, weight, and whether the element is text at all.
The ratio is a luminance comparison, not a color comparison
Contrast ratio is defined as (L1 + 0.05) / (L2 + 0.05), where L1 is the relative luminance of
the lighter color and L2 of the darker one. Relative luminance itself is a weighted sum of the
gamma-corrected RGB channels, weighted roughly 21% red, 72% green, 7% blue.
Two consequences fall out of that formula:
- Green dominates. A saturated green and a saturated blue of the same "visual intensity" have very different luminance. This is why blue text on black often fails while green text on black passes.
- Hue is irrelevant. Red on green scores well on contrast and is still unusable for a red-green colorblind user. Contrast ratio is necessary, not sufficient.
The range is 1:1 (identical colors) to 21:1 (pure black on pure white).
The four thresholds that matter
- 4.5:1 — AA for normal body text. This is the default you should design to.
- 3:1 — AA for large text, defined as 18.66px bold or 24px regular and up. Also the AA threshold for UI components and graphical objects: input borders, focus rings, icon shapes that carry meaning.
- 7:1 — AAA for normal text.
- 4.5:1 — AAA for large text.
Note the overlap: 4.5:1 is simultaneously AA-normal and AAA-large. A single number does not tell you which criterion you passed, which is why a checker should report all four.
Where audits actually fail
Placeholder text. Designers lighten placeholders to visually de-emphasize them. Placeholders are text and must hit 4.5:1. If it looks too heavy at that ratio, the fix is a visible label, not a lighter gray.
Disabled controls. WCAG exempts inactive controls from contrast requirements, so a low-contrast disabled button is technically conformant — but users routinely cannot tell "disabled" from "broken." Pair the low contrast with another signal.
Text over images and gradients. The ratio must hold at the worst pixel behind the text, not the average. Test against the lightest region of a photo and the lightest stop of a gradient. A scrim or solid text container is usually cheaper than hunting for a color that survives every background.
Focus indicators. A focus ring needs 3:1 against the adjacent background. Default browser rings
often fail against colored surfaces, and removing outline without a replacement is a
straightforward failure.
Brand colors used as text. Mid-tone brand colors — most oranges, yellows, and light blues — land around 2:1 to 3:1 on white. They work as fills with dark text on top, not as text themselves. Keep a darkened variant of the brand color reserved for text.
A workable process
- Fix your text colors first, at 4.5:1 or better against every surface you support.
- Check UI elements separately at 3:1. This is the step most teams skip entirely.
- Repeat the whole pass for dark mode. Inverted palettes rarely inherit the same ratios — a gray that reads well on white is usually too dim on near-black.
- Verify borderline pairs at the exact hex values that ship, not the approximations in the mockup.
Try it
- Contrast Checker — see all four AA/AAA verdicts for a foreground and background pair, with a live text preview
- Color Converter — get the exact HEX from an RGB or HSL value before testing
- Palette Generator — build lightness variants of a brand color to find one that passes as text
Related reading: HEX vs RGB vs HSL covers why HSL is the right format for producing those darker text-safe variants.