The short version
- Color contrast is the difference in brightness between text and its background, measured as a ratio from 1:1 (no difference) to 21:1 (black on white).
- WCAG 2 level AA asks for at least 4.5:1 for normal text and 3:1 for large text, which is about 24 px regular or 18.66 px bold.
- Icons, input borders and other parts of an interface people need to see also need at least 3:1 against what surrounds them.
- On white,
#767676is the lightest plain grey that passes 4.5:1 for body text, and#777777just fails. - On a gradient, contrast changes from point to point, so you must test the worst spot behind the words, not the average colour.
On this page
Color contrast is the difference in brightness between text and the colour behind it, and it decides whether people can read your words at all. The Web Content Accessibility Guidelines (WCAG) measure it as a ratio from 1:1, where the two colours are identical, to 21:1, black on white. For level AA, normal text needs at least 4.5:1 and large text at least 3:1. Everything else in this guide is about hitting those numbers without making your design timid.
Contrast matters for everyone, not only for people with low vision. Phones in sunlight, dim projectors, cheap monitors and tired eyes all shrink the difference between colours. Designs with enough contrast simply get read more.
The WCAG contrast ratios in plain words
Three rules in WCAG 2 cover almost every design decision. They come from the W3C's success criteria, and they are what most accessibility audits and regulations refer to.
| What | Level AA | Level AAA | Notes |
|---|---|---|---|
| Normal text | 4.5:1 | 7:1 | Body copy, captions, labels, buttons |
| Large text | 3:1 | 4.5:1 | At least 18 pt (about 24 px), or 14 pt bold (about 18.66 px) |
| Icons and interface parts | 3:1 | 3:1 | Input borders, chart lines that carry meaning, icons without labels |
| Logos and decoration | None | None | Brand logotypes and purely decorative text are exempt |
Two details catch people out. First, "large" is about the rendered size, so a 24 px heading on a phone that scales it down may stop counting as large. Second, a disabled button is exempt, but a light grey placeholder that people need to understand is not.
How the contrast ratio is calculated
The ratio compares the relative luminance of the two colours: how much light each one gives off, weighted the way the eye sees. Green contributes most, red less and blue very little, which is why a saturated blue can be surprisingly dark and a yellow surprisingly light. The formula is (L1 + 0.05) / (L2 + 0.05), where L1 is the lighter colour.
// WCAG 2 contrast ratio between two hex colours
const channel = (c) => {
c /= 255
return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4
}
const luminance = (hex) => {
const [r, g, b] = [1, 3, 5].map((i) => channel(parseInt(hex.slice(i, i + 2), 16)))
return 0.2126 * r + 0.7152 * g + 0.0722 * b
}
const contrast = (a, b) => {
const [hi, lo] = [luminance(a), luminance(b)].sort((x, y) => y - x)
return (hi + 0.05) / (lo + 0.05)
}
contrast('#767676', '#ffffff') // 4.54, passes for body text
contrast('#777777', '#ffffff') // 4.48, failsYou do not need to calculate it by hand. The WebAIM contrast checker takes two hex codes and tells you which levels pass, and the colour picker in Chrome's developer tools shows the ratio of any text you inspect.
Colour pairs that pass and fail
Brand colours often sit in the awkward middle, too dark for dark text and too light for white text. Medium greens, oranges, pinks and light blues are the usual culprits. These are common pairings, with their real ratios.
| Text on background | Ratio | Normal text | Large text |
|---|---|---|---|
White on #7c3aed violet | 5.7:1 | Pass | Pass |
White on #8b5cf6 light violet | 4.2:1 | Fail | Pass |
White on #2563eb blue | 5.2:1 | Pass | Pass |
White on #3b82f6 sky blue | 3.7:1 | Fail | Pass |
White on #10b981 green | 2.5:1 | Fail | Fail |
#0b0b10 near-black on #10b981 green | 7.7:1 | Pass | Pass |
White on #f59e0b amber | 2.2:1 | Fail | Fail |
#999999 grey on white | 2.9:1 | Fail | Fail |
#6b7280 grey on white | 4.8:1 | Pass | Pass |
Why gradients make contrast harder
A flat background has one contrast ratio. A gradient has a different one at every point. White text that passes easily over the dark end of a blend can fail completely at the light end, and a headline that stretches across the gradient passes and fails in the same line.
linear-gradient(90deg, #1f0b3a 0%, #7c3aed 55%, #f472b6 100%)The rule is simple: test the lightest point behind light text, or the darkest point behind dark text. If the worst point passes, the whole line passes. Average colours and eyedropper samples from the middle will mislead you.
How to fix contrast on a gradient
- 1
Move the words to the calm end
Place text over the darkest or lightest part of the gradient, and let the bright transition happen away from it.
- 2
Narrow the range behind the text
Keep the part of the gradient that sits behind words within a small lightness range. The rest of the canvas can be as bold as you like.
- 3
Add a soft scrim
A gentle dark or light fade behind the text, such as a transparent-to-30% black gradient, lifts contrast without a visible box.
- 4
Go bigger and bolder
Large text only needs 3:1. A heavier weight and a larger size can turn a failing line into a passing one.
- 5
Test on a real phone
Check the design at full brightness and at low brightness, outdoors if you can. Ratios are the floor, not the goal.
.card {
background: linear-gradient(90deg, #1f0b3a 0%, #7c3aed 55%, #f472b6 100%);
color: #ffffff;
}
/* A soft scrim keeps white text above 4.5:1 across the pink end */
.card .text {
background: linear-gradient(90deg, transparent 0%, rgb(31 11 58 / 0.55) 60%);
padding: 1rem 1.25rem;
}The words run straight over the brightest part of the field and their contrast drops.
The same design with the calm, dark region moved behind the words.
This is the idea behind Gradiently's legibility: a Mark knows where your words are, moves its calmest, best-contrasting region behind them and picks light or dark ink per line from its own palette. For more techniques that work anywhere, read text on a gradient and readable text over photos. For dark interfaces specifically, see designing for dark mode.
Questions people ask
What is a good color contrast ratio?
At least 4.5:1 for normal text and 3:1 for large text meets WCAG level AA. For long reading, 7:1 (level AAA) is more comfortable.
What counts as large text in WCAG?
Text of at least 18 point, about 24 px, or at least 14 point bold, about 18.66 px. Large text only needs 3:1 at level AA.
What is the lightest grey I can use on white?
#767676 is the lightest plain grey that reaches 4.5:1 on white. #777777 measures 4.48:1 and fails for body text.
Do logos need to meet contrast requirements?
No. WCAG exempts text that is part of a logo or brand name, and purely decorative text. Everything people need to read or use still counts.
How do I check contrast on a gradient background?
Measure the text colour against the lightest point behind light text, or the darkest point behind dark text. If that worst point passes, the whole line passes.
Written by Gradiently
The team behind Gradiently, a design tool built around Marks: living gradients that make everything you design look like yours.
See our profile


