WCAG contrast requirements
| Content | AA (minimum) | AAA (enhanced) | Criterion |
|---|---|---|---|
| Normal text | 4.5:1 | 7:1 | 1.4.3 / 1.4.6 |
| Large text (≥ 24px, or ≥ 18.66px bold) | 3:1 | 4.5:1 | 1.4.3 / 1.4.6 |
| UI components, focus rings, icons, chart lines | 3:1 | – | 1.4.11 |
| Disabled controls, logos, decoration | No requirement | – | |
Common colors on white
| Color | Ratio | AA text | AA large | Note |
|---|---|---|---|---|
#767676
|
4.54:1 | Pass | Pass | The lightest gray that passes AA on white |
#777777
|
4.47:1 | Fail | Pass | Looks the same, but fails AA (4.47:1) |
#595959
|
7.00:1 | Pass | Pass | The lightest gray that passes AAA on white |
#64748b
|
4.75:1 | Pass | Pass | Tailwind slate-500 |
#9ca3af
|
2.53:1 | Fail | Fail | Tailwind gray-400, a common placeholder color |
#3b82f6
|
3.67:1 | Fail | Pass | Tailwind blue-500 |
#2563eb
|
5.16:1 | Pass | Pass | Tailwind blue-600 |
#ff6600
|
2.93:1 | Fail | Fail | Bright orange |
Fixing low contrast
- Change lightness, not hue. The suggestions above keep the OKLCH hue and chroma and only move lightness, so the color still reads as your brand color.
- Use one shade darker for text. In most palettes the 500 shade is for fills and the 600 or 700 shade is for text on white.
- Check both themes. A gray that passes on white often fails on a dark background. Test each pair in light and dark mode.
- Don’t rely on opacity. Gray text made with
opacity: 0.6changes contrast with every background it lands on. Use a solid color instead.
Checking contrast in code
// Colors as [r, g, b] in 0-255
function luminance([r, g, b]) {
const lin = (c) => {
c /= 255;
return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
};
return 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b);
}
function contrast(a, b) {
const [hi, lo] = [luminance(a), luminance(b)].sort((x, y) => y - x);
return (hi + 0.05) / (lo + 0.05);
}
contrast([118, 118, 118], [255, 255, 255]); // 4.54 -> passes AA
Automated checkers like axe-core and Lighthouse use the same formula. Need the color in another format? Open the color converter.
Frequently asked questions
What contrast ratio do I need for WCAG AA?
WCAG 2.2 level AA requires at least 4.5:1 for normal text and 3:1 for large text (24px, or 18.66px bold). User interface components, focus indicators and meaningful icons need 3:1 against adjacent colors (success criterion 1.4.11). Level AAA raises text to 7:1 and large text to 4.5:1.
How is the contrast ratio calculated?
Each color’s relative luminance L is computed from its linearised sRGB channels as 0.2126 R + 0.7152 G + 0.0722 B. The ratio is (L1 + 0.05) / (L2 + 0.05), with L1 the lighter color. It ranges from 1:1 (same color) to 21:1 (black on white). Translucent text is first blended with the background.
Why does #777 fail when it looks fine?
#777777 on white is 4.47:1, just under 4.5. WCAG thresholds are strict, so 4.49:1 fails. This tool truncates the ratio instead of rounding it, so a failing pair never displays as 4.5:1. The nearest passing gray is #767676 (4.54:1).
What about APCA and WCAG 3?
APCA is a newer contrast method being explored for WCAG 3. It accounts for font size and weight and for the difference between dark-on-light and light-on-dark text. WCAG 3 is still a draft, and laws and procurement rules (ADA, Section 508, EN 301 549) reference WCAG 2.x, so the 4.5:1 rule is what audits check today.
Do disabled buttons and logos need to pass?
No. WCAG exempts text in inactive (disabled) controls, purely decorative text, text in logos and brand names, and text that is part of a picture with significant other content. Placeholder text in form fields is not exempt, and is a very common failure.