Web development

WCAG colour contrast: how it is calculated, which ratios it requires and how to meet them

What the contrast ratio is, how it is calculated from luminance, what levels AA and AAA require, how to measure it and the design mistakes that fail it.

Colour contrast is the difference in brightness between text (or a graphic element) and its background, expressed as a ratio from 1:1 (no contrast) to 21:1 (black on white). WCAG, the web accessibility standard, requires at least 4.5:1 for normal text and 3:1 for large text and user interface components at level AA, and 7:1 at level AAA. It is not an aesthetic preference: a light grey on white that looks elegant to you is unreadable for a real share of visitors, and Lighthouse, axe and any audit will flag it.

This article explains how the ratio is calculated, what each criterion requires, how to measure it and how to fix the typical cases without redoing the palette.

How the ratio is calculated

The formula is in the WCAG definition:

ratio = (L1 + 0.05) / (L2 + 0.05)

where L1 is the relative luminance of the lighter colour and L2 that of the darker one, both between 0 (black) and 1 (white). Luminance comes from the sRGB components: each channel is normalised to 0–1, linearised (if ≤ 0.04045 it is divided by 12.92; otherwise ((c + 0.055) / 1.055) ^ 2.4) and combined with the weights 0.2126 R + 0.7152 G + 0.0722 B. Green weighs far more than blue because the human eye is more sensitive to it: that is why a saturated blue on black contrasts less than it seems.

In JavaScript:

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 [l1, l2] = [luminance(a), luminance(b)].sort((x, y) => y - x);
  return (l1 + 0.05) / (l2 + 0.05);
}
contrast([29, 29, 31], [255, 255, 255]); // #1d1d1f on white ≈ 16.8:1
contrast([110, 110, 115], [255, 255, 255]); // #6e6e73 on white ≈ 5.1:1
contrast([134, 134, 139], [255, 255, 255]); // #86868b on white ≈ 3.6:1 (fails for normal text)

The three examples are this blog’s greys: the first is the main text, the second the grey we use for secondary text (passes AA) and the third the Apple grey we started with and had to drop for small text.

What each criterion requires

Criterion Level Applies to Minimum ratio
1.4.3 Contrast (Minimum) AA Normal text 4.5:1
1.4.3 Contrast (Minimum) AA Large text (≥ 24 px, or ≥ 18.66 px bold) 3:1
1.4.6 Contrast (Enhanced) AAA Normal text 7:1
1.4.6 Contrast (Enhanced) AAA Large text 4.5:1
1.4.11 Non-text Contrast AA User interface components (field borders, required icons, focus indicators) and informative graphics 3:1

Exempt: decorative text, logos, text in disabled controls and text that is part of a purely decorative image. A form field placeholder is not exempt: it is text the user needs to read.

“Large” is defined in points (18 pt, or 14 pt bold); in CSS that is roughly 24 px and 18.66 px.

How to measure it

  • Colour tool on AIMRAN Tools: enter the text colour and the background colour (in HEX, RGB, HSL or OKLCH) and it shows the ratio and whether it passes AA and AAA for normal and large text. It runs in the browser.
  • Chrome DevTools: when inspecting text, the colour picker in the Styles panel shows the ratio and two lines on the colour wheel marking where AA and AAA begin. The CSS Overview panel lists every low-contrast pair on the page.
  • Lighthouse / axe: the color-contrast audit lists the failing elements with their current ratio.
  • Firefox: the accessibility inspector has a contrast check with colour-blindness simulation.

Automatic tools do not catch text over images or gradients: there you have to measure against the lightest (or darkest) area of the background that can end up under the text.

The most frequent failures and how to fix them

  1. “Subtle” grey for secondary text. #999 on white gives 2.9:1. Go down to #6e6e73 (5.1:1) or #595959 (7:1). Hierarchy also comes from size and weight, not only colour.
  2. White text on light brand colours. White on #0071e3 gives 4.7:1 (passes); on #34c759 (green) only 2.2:1. For labels in light colours, use dark text or mix the colour with black: on this blog, category labels use color-mix(in srgb, var(--c) 62%, black) for text and the pure colour only in icons and backgrounds.
  3. Links distinguished by colour alone. Besides contrast with the background, a link inside a paragraph must differ from normal text (WCAG asks for 3:1 between the two or an additional cue, such as underlining). Underlining is the simplest.
  4. Light placeholders. The default grey in many browsers does not reach 4.5:1. Set ::placeholder { color: #6e6e73 }.
  5. Invisible focus indicator. The keyboard outline must have 3:1 against the adjacent background. A outline: 2px solid in the primary colour on a white background usually passes; on a button of the same colour it does not: add an outline-offset so the ring sits over the background.
  6. Dark mode not reviewed. Greys that work on white do not work on #000. Measure both palettes separately.

A note on APCA

WCAG 3, still a draft, is studying a different algorithm (APCA) that better accounts for polarity (light text on dark versus dark on light) and font size. It is an interesting design reference, but the legal requirement and the one audits measure today is the WCAG 2 ratio, so that is the one you must meet.

Conclusion

Contrast is calculated with a fixed formula from the luminance of the two colours and compared with three thresholds: 4.5:1 for text, 3:1 for large text and components, 7:1 if you aim for AAA. Measure with DevTools or a contrast tool, review light and dark separately and remember that visual hierarchy does not depend on grey alone: size, weight and spacing do the same job without leaving anyone out.

Sources and references

  1. WCAG 2.2: Success Criterion 1.4.3 Contrast (Minimum) w3.org
  2. WCAG 2.2: Success Criterion 1.4.6 Contrast (Enhanced) w3.org
  3. WCAG 2.2: Success Criterion 1.4.11 Non-text Contrast w3.org
  4. WCAG 2.2: definition of contrast ratio and relative luminance w3.org
  5. Chrome Lighthouse: Background and foreground colors do not have a sufficient contrast ratio developer.chrome.com

Related tools

Herramientas gratuitas de AIMRAN Tools que funcionan en tu navegador, sin registro.

Ver todas las herramientas