Design

Color contrast and accessibility: what WCAG requires

That elegant light gray on white looks readable on your new screen, but it can vanish for someone with low vision, for a reader in direct sunlight or on a cheap monitor. WCAG settles the argument with an objective number: the contrast ratio, which runs from 1:1 (identical colors) to 21:1 (black on white). This guide derives that number step by step, sRGB linearization, relative luminance, the 0.05 constant, pins down the exact WCAG 2.2 AA and AAA thresholds, and then does what most write-ups avoid: it shows where the formula fails and why a successor, APCA, exists but has replaced no one yet. Paste two colors into the [contrast checker](tool:verificador-contraste) as you read, it computes everything in the browser and suggests a nearby passing tone when yours fails.

J-Kit15 min readIntermediate
  • Accessibility
  • WCAG
  • Contrast
  • Design
  • Color

Key takeaways

  • The contrast ratio runs from 1:1 (identical colors) to 21:1 (black on white); for normal text, AA requires 4.5:1 and AAA requires 7:1.
  • Large text (18pt ≈ 24px, or 14pt bold ≈ 18.5px) and UI components have a lower floor of 3:1.
  • The formula has known defects: it ignores polarity (light-on-dark vs dark-on-light) and treats font size/weight as steps, not continuously.
  • APCA is the perceptual candidate for WCAG 3, but it is not normative in any W3C document today, WCAG 3 is still a draft and states that its contrast algorithm is undetermined.

Why contrast is a number, not an opinion

Readability is not a matter of taste. Text you read effortlessly in a dim office can vanish for a person with low vision, for someone with cataracts, for the same user under direct sunlight or on a phone dimmed to save battery. That is why WCAG (the W3C Web Content Accessibility Guidelines) does not ask for pleasant colors, it defines the contrast ratio, a single reproducible value that every tool computes the same way, from the same constants.

The ratio compares the perceived brightness of two colors, text and background, and returns a number between 1:1 and 21:1. Identical colors give 1:1 (invisible); pure black on pure white gives the maximum, 21:1. Everything else sits in between, and the WCAG thresholds say where to draw the line. But note this up front: the number is honest about luminance and misleading about perception. It gets most cases right and a few predictably wrong, and the second half of this guide is about those errors.

1:1Identical colors (minimum)
4.5:1AA for normal text
7:1AAA for normal text
21:1Black on white (maximum)

The derivation: relative luminance and the ratio

The math has three stages. First, each sRGB channel (0–255) is normalized to 0–1 and linearized, you undo the transfer curve (the gamma correction) the screen applies, to get back to physical light. That curve has a knee: below a tiny threshold it is a straight line (divide by 12.92), above it a power with exponent 2.4. Then the three linear channels become a relative luminance, with very different per-channel weights because the eye sees green far more than blue. Finally, the ratio combines the two luminances, adding 0.05 to each.

lin(c) = c / 12.92 se c <= 0.03928 lin(c) = ((c + 0.055) / 1.055) ^ 2.4 caso contrario / otherwise L = 0.2126 * R + 0.7152 * G + 0.0722 * B contraste = (L_claro + 0.05) / (L_escuro + 0.05)
c
channel value (R, G or B) normalized to 0–1
0.03928
the knee of the sRGB curve, verbatim from the normative WCAG text (strict sRGB now uses 0.04045; the gap vanishes at 8 bits)
2.4
exponent that undoes sRGB gamma and returns linear light
0.2126 / 0.7152 / 0.0722
luminance weights for R, G and B (green dominates; blue barely counts)
0.05
assumed ambient reflection (screen flare): without it, black on black would divide by zero
Channel linearization, relative luminance and the WCAG 2.x contrast ratio, exactly the formulas the checker implements.

Notice green: it weighs 0.7152, nearly three quarters of the brightness, while blue weighs only 0.0722. That is why vivid-blue text on a dark background often fails even when it looks vibrant: it contributes almost nothing to luminance. And because the ratio always puts the lighter color on top, the text/background order does not change the number, the contrast of A over B equals that of B over A. Hold on to that symmetry: it is convenient to compute and, as we will see, it is one of the formula’s defects. If the link between channels, hue and lightness still feels fuzzy, the color spaces: HEX, RGB and HSL guide shows how to move brightness without breaking the hue.

Two worked examples: the gray that fails by a hair

Let us derive one concrete color down to the final number. Take gray #767676 as text on a white #FFFFFF background, a tone that looks perfectly legible to the naked eye and that WCAG itself uses as a boundary example. Each channel is 0x76 = 118, so we normalize 118/255 = 0.46275, apply the linearization (the power branch, since 0.46275 > 0.03928) and reach the luminance. White has all three channels at 1.0, so its luminance is 1.0.

// Exemplo 1, #767676 (texto) sobre #FFFFFF (fundo)
// Example 1, #767676 (text) on #FFFFFF (background)

Canal / channel:  0x76 = 118  ->  118 / 255 = 0.46275
  0.46275 > 0.03928  ->  ((0.46275 + 0.055) / 1.055) ^ 2.4 = 0.18116

L(#767676) = 0.2126*0.18116 + 0.7152*0.18116 + 0.0722*0.18116
           = (0.2126 + 0.7152 + 0.0722) * 0.18116  =  0.18116
L(#FFFFFF) = 1.00000

contraste = (1.00000 + 0.05) / (0.18116 + 0.05)
          = 1.05 / 0.23116  =  4.5422  ->  4.54:1

// 4.54 >= 4.5  ->  PASSA no AA de texto normal (por uma unha)
// 4.54 >= 4.5  ->  PASSES AA normal text (by a hair)
Full derivation of #767676 on white: 4.54:1. Since R=G=B, the weights sum to 1 and the luminance equals the linearized channel.

One step lighter, #777777, drops to 4.478:1, and here lives a subtlety that separates people who understand WCAG from people who memorize it. The norm says do not round: 4.478 is less than 4.5, so it fails, even though your head wants to round to 4.48. The checker goes further and truncates the display to 4.47, making it explicit that it sits below the floor. So #777777 fails normal text by hundredths, and darkening a single step to #767676 already clears the line. For a large heading, though, both pass comfortably, because the floor drops to 3:1.

Four grays on white (#FFFFFF), with the exact ratio the checker shows.
Text colorRatioVerdict
#7777774.47:1Fails AA normal text (4.478 < 4.5); passes large text and UI.
#7676764.54:1Passes AA normal text by 0.04; fails AAA.
#6666665.74:1Passes AA; still fails AAA (< 7).
#5959597.00:1Clears AAA, the lightest gray that still reaches 7:1 on white.
Blue #00F on black2.44:1
Gray #949494 on white3.03:1
Gray #777 on white4.47:1
White on blue #0D6EFD4.5:1
Gray #767676 on white4.54:1
Gray #666 on white5.74:1
Gray #595959 on white7:1
Contrast ratio (computed with the WCAG formula) of real combinations, against the 3:1 (large/UI), 4.5:1 (AA text) and 7:1 (AAA text) thresholds. Bars below 3 fail everything; between 3 and 4.5 they serve only large text and components.
View the data
CategoryValue
Blue #00F on black2.44:1
Gray #949494 on white3.03:1
Gray #777 on white4.47:1
White on blue #0D6EFD4.5:1
Gray #767676 on white4.54:1
Gray #666 on white5.74:1
Gray #595959 on white7:1

The chart makes the lesson visible: a saturated blue on black (2.44:1) fails everything despite looking intense, because blue barely produces luminance; and there is a narrow band, between 4.47 and 4.54, where two near-identical grays land on opposite sides of the 4.5 line. Now try to reproduce any bar: enter the two colors in the embedded checker below and read the ratio and the verdict for AA, AAA and components.

Enter text and background (HEX, RGB or a CSS name): the tool computes the ratio locally, shows AA/AAA/UI and suggests a nearby tone that passes 4.5:1.Open the tool full page
  1. Pick the two colorsEnter the text and background colors as HEX, RGB or a CSS name. Use the color converter if you need to translate between formats.
  2. Read the ratio and verdictCheck whether it passes at the level and size you use: AA normal (4.5), AA large (3), AAA (7) or UI (3).
  3. Adjust if it failsDarken (or lighten) the text until it clears the threshold; the tool suggests a nearby tone that passes 4.5:1, preserving the hue.

WCAG 2.2 thresholds and their history

Four WCAG success criteria involve color and contrast. 1.4.3 Contrast (Minimum) is Level AA and covers text (4.5:1 normal, 3:1 large); 1.4.6 Contrast (Enhanced) is the stricter AAA (7:1 normal, 4.5:1 large); 1.4.11 Non-text Contrast (AA) requires 3:1 for input borders, essential icons, focus states and graphical objects; and 1.4.1 Use of Color (Level A) forbids using color as the only means of conveying information. Large text gets a lower floor because bigger letters stay legible with less contrast.

Color- and contrast-related success criteria (WCAG 2.2).
CriterionLevelTargetApplies to
1.4.1 Use of ColorAColor must not be the only cueLinks, states, charts, forms
1.4.3 Contrast (Minimum)AA4.5:1 normal · 3:1 largeText and images of text
1.4.6 Contrast (Enhanced)AAA7:1 normal · 4.5:1 largeText and images of text
1.4.11 Non-text ContrastAA3:1UI components and graphical objects
What counts as large text
Per WCAG, at least 18 point (with 1pt = 1.333px, that is ~24 CSS px) at normal weight, or 14 point bold (~18.5px). Below that, the normal-text thresholds apply. Since type scales fluidly today, see CSS clamp and fluid typography so you do not accidentally drop below the cutoff.
Do not round the ratio
WCAG is explicit: a ratio of 4.499:1 fails the 4.5:1 floor. That is why a gray like #777777 (4.478) fails, and why the checker truncates the display instead of rounding up.
AA vs AAA
AA is the practical target for most sites and most accessibility laws. AAA is the ideal for critical content, but it is not always achievable across an entire interface, the W3C itself does not recommend requiring AAA of a whole site.
  1. 1999WCAG 1.0

    The first version measured color by brightness and hue difference, not by the luminance ratio, a method now abandoned.

  2. 2008WCAG 2.0 introduces the ratio

    The recommendation defines relative luminance and criteria 1.4.3 (AA, 4.5:1) and 1.4.6 (AAA, 7:1). It is the formula we still use.

  3. 2018WCAG 2.1 and non-text

    Adds 1.4.11 Non-text Contrast (3:1), extending the rule to borders, icons and focus states.

  4. 2023WCAG 2.2, and APCA out of the draft

    WCAG 2.2 becomes a recommendation without changing the contrast formulas; the same year, APCA is removed from the WCAG 3 draft for further evaluation.

Where the formula fails, and what APCA is

The WCAG 2.x ratio is solid 2008 engineering with defects that are, today, well documented. It measures a luminance proportion, but human legibility is not a pure proportion: it depends on polarity, on font size and weight, and on the brightness the eye is adapted to. The formula models none of that continuously, so it errs in two directions, it passes hard-to-read combinations and fails some perfectly legible ones.

The second worked example shows the cleanest defect: polarity blindness. We saw that #767676 on white gives 4.54:1 and passes AA. Now flip it, white on a #767676 background. WCAG returns exactly the same 4.54:1, because the formula is symmetric: it puts the lighter color on top and ignores which is text and which is background. Yet, to the eye, dark text on a light background (positive polarity) usually reads better than light text on a mid gray at the same luminance difference. Two pairs, same number, different legibility, and WCAG 2 has no way to express it.

It passes the illegible: vivid red on black

Pure red #FF0000 on black gives 5.25:1 and passes AA for normal text. But saturated red on black vibrates and tires: the eye has low acuity for pure red and blue, and the missing supporting luminance makes edges shimmer. WCAG only sees the luminance proportion (red carries 0.2126 of it) and misses the perceptual cost of saturation. Fine on paper, rough on screen.

It fails the legible: the dark-mode hole

Because of the 0.05 and the shape of the curve, WCAG 2 compresses differences at the dark end. Light-text-on-dark pairs that a dark-adapted eye reads comfortably can get a low ratio and fail, while other dark pairs pass without earning it. The research behind APCA grew out of measuring exactly this discrepancy: in the dark, the luminance proportion stops predicting real reading.

APCA and WCAG 3: the real status

APCA (Accessible Perceptual Contrast Algorithm) is a perceptual approach: instead of a symmetric proportion, it produces a signed value (Lc) that changes with polarity and pairs with font size and weight tables. It was developed as a candidate for the contrast in the future WCAG 3 (the Silver project).

Be honest about the status: APCA is not normative in any W3C document today. In July 2023 the visual-contrast content (including APCA) was removed from the WCAG 3 draft for further evaluation, and the current draft states that WCAG 3’s contrast algorithm is yet to be determined. WCAG 3 remains a Working Draft in 2026, with a final recommendation projected no earlier than 2028. So APCA has not replaced WCAG 2, for conformance and for the law, this guide’s formula (4.5:1, 7:1, 3:1) is the one that counts.

Contrast does not solve color blindness

Passing contrast is necessary but not sufficient. About 8% of men and 0.5% of women of Northern European descent have some form of red-green color blindness, the National Eye Institute sums it up as roughly 1 in 12 men. The inheritance is X-linked: a man has a single X, so one faulty gene is enough; a woman has two, and the second usually compensates, which is why the prevalence collapses. For those people, two colors can have high contrast with each other and still be indistinguishable when the information depends on hue alone.

The classic case is a chart that separates gain and loss only by green and red, or a link that differs from body text only by being blue. That is why criterion 1.4.1 Use of Color (Level A) requires that color never be the only visual means of conveying information: add an underline to the link, an icon to the error message, a label to the chart slice, a pattern to the filled area. Test how your colors look under protanopia, deuteranopia and tritanopia in the color blindness simulator before locking the palette, and remember photos carry color too: the web image formats guide touches on profiles and gamma, which affect how color reaches the screen.

  • Body text reaches 4.5:1 (AA); large headings at least 3:1, with no rounding up.
  • Input borders, essential icons and focus state have ≥ 3:1 (1.4.11).
  • No information relies on color alone (1.4.1): there is a supporting icon, text or pattern.
  • The palette was checked in a color blindness simulator (protan, deuteran, tritan).
  • Dark-mode pairs were tested with real eyes, not just the WCAG 2 ratio.

Frequently asked questions

What is the minimum contrast for text?
For normal text, WCAG 2.2 requires 4.5:1 at Level AA and 7:1 at AAA. Large text (18pt ≈ 24px, or 14pt bold ≈ 18.5px) needs 3:1 at AA and 4.5:1 at AAA. The norm says do not round: 4.499:1 fails the 4.5:1 floor.
Does gray #777 pass on a white background?
Not for normal text: #777777 on #FFFFFF gives 4.478:1, below the required 4.5:1 (the checker shows 4.47, truncated). It only passes as large text or a UI component (3:1 threshold). Darkening one step to #767676 rises to 4.54:1 and clears AA.
Do icons and borders need contrast too?
Yes. Criterion 1.4.11 (Non-text Contrast, Level AA) requires at least 3:1 for essential interface components, input borders, meaningful icons, focus indicators, and for graphical objects needed to understand the content. It was added in WCAG 2.1, in 2018.
Will APCA replace the WCAG 2 formula?
Maybe someday, but not today. APCA is the perceptual candidate for WCAG 3, yet it is normative in no W3C document. In 2023 the contrast content was removed from the WCAG 3 draft, which declares the algorithm still undetermined; WCAG 3 remains a draft, with no recommendation before 2028. For conformance, use WCAG 2.2.
Does good contrast alone guarantee color accessibility?
No. Two colors can have high contrast and still be confused by someone with color blindness if the information depends on hue alone, which affects about 1 in 12 men. Criterion 1.4.1 requires an extra cue (icon, text, pattern) whenever color carries meaning.
Why does the formula add 0.05 to the luminances?
The 0.05 represents the ambient light the screen reflects: no real black is fully black. Adding it to both luminances avoids division by zero and caps the ratio at 21:1. The side effect is that, at the dark end, that term dominates and the ratio loses resolution, the origin of WCAG 2’s weak behavior in dark mode.

Treat contrast as a number, not an aesthetic: the ratio (1:1 to 21:1) comes from relative luminance, with 4.5:1 for normal text, 3:1 for large text and components, 7:1 at AAA, and no rounding up. But know the formula’s limits: it ignores polarity and treats size as steps, so it passes some illegible pairs and fails some good ones. APCA was born to fix this, but it is still a draft; WCAG 2.2 is what counts today. And color must never be the only cue, add an icon, text or pattern, and check the palette in a color blindness simulator.

Sources & references

  1. W3C, WCAG 2.2, 1.4.3 Contrast (Minimum)
  2. W3C, WCAG 2.2, 1.4.6 Contrast (Enhanced)
  3. W3C, WCAG 2.2, 1.4.11 Non-text Contrast
  4. W3C, WCAG 2.2, relative luminance definition
  5. W3C, WCAG 2.2, 1.4.1 Use of Color
  6. W3C, WCAG 3.0 (Working Draft): contrast algorithm undetermined
  7. National Eye Institute, Color Blindness