Skip to contentAltstamp
Menu

Colour contrast requirements explained

Updated . An 11 minute read.

Line drawing of two standing swatch cards, one pale grey and one near black, the dark one ringed in orange
Fig. 1A pale grey swatch and a near black swatch stand side by side on white cards, and the strong one is ringed in orange

Three numbers on one scale, from 1:1 (white on white) to 21:1 (black on white)

  • 7:1 AAABody text at Level AAA (1.4.6).Lightest grey that reaches it on white: #595959, 7.00:1.
  • 4.5:1 AABody text at Level AA (1.4.3), and large text at AAA (1.4.6).Lightest grey that reaches it on white: #767676, 4.54:1.
  • 3:1 AALarge text at Level AA (1.4.3: 18 point, or 14 point bold), and the parts of controls, focus rings and graphs (1.4.11).Lightest grey that reaches it on white: #949494, 3.03:1.

Each sample is drawn at a size where its grey is allowed. A ratio just under a line fails it: WCAG does not round.

The three rules and the numbers in them

Three success criteria hold every contrast number in WCAG. 1.4.3 Contrast (Minimum), Level AA: text needs a contrast ratio of at least 4.5:1; large-scale text at least 3:1. Logos and decorative or inactive text are exempt. 1.4.6 Contrast (Enhanced), Level AAA: at least 7:1 for text; at least 4.5:1 for large-scale text. The third covers everything that is not text.

What WCAG asks: 1.4.11 Non-text Contrast, Level AA

The visual presentation of the following have a contrast ratio of at least 3:1 against adjacent color(s):

  • User Interface Components Visual information required to identify user interface components and states, except for inactive components or where the appearance of the component is determined by the user agent and not modified by the author;
  • Graphical Objects Parts of graphics required to understand the content, except when a particular presentation of graphics is essential to the information being conveyed.

"Large-scale" text is at least 18 point, or 14 point bold. W3C's Understanding note puts that at about 24px and 18.5px (1pt = 1.333px). The size is the one the page delivers, not what a visitor gets after zooming, and the definition warns that very thin or unusual typefaces are harder to read at lower contrast. That is why a light-weight 24px heading can pass the number and still be hard to read; the guide to fonts covers weight and size.

Level AA means every Level A and Level AA criterion is met, so a site that meets WCAG 2.1 or 2.2 Level AA needs 1.4.3 and 1.4.11, not 1.4.6. W3C does not recommend requiring AAA for whole sites. Some brands still hold body text to 7:1, as this site does, because it costs nothing once the palette is set.

WCAG 2.2 contrast thresholds, where each one applies, at Level AA and AAA
Where it appliesAAAAA
Body textParagraphs, links, labels, menus, button words, placeholder text1.4.3 and 1.4.64.5:17:1
Large textAt least 18 point, or 14 point bold: about 24px and 18.5px1.4.3 and 1.4.63:14.5:1
Text on a photo or gradientMeasured against the part of the picture behind the letters1.4.3 and 1.4.64.5:1 or 3:17:1 or 4.5:1
Controls and focus ringsInput borders, checkboxes, toggles, icon buttons, the focus indicator1.4.113:1No AAA rule
Graphs and iconsLines, bars, slices and symbols needed to understand the content1.4.113:1No AAA rule
Logos and brand namesText that is part of a logo1.4.3NoneNone
Disabled and decorativeInactive controls, pure decoration, a street sign in a photo1.4.3 and 1.4.11NoneNone

A site that meets WCAG 2.1 or 2.2 Level AA needs the AA column.

Where 4.5:1 and 3:1 apply to text, and where they do not

The text rule reaches further than most people expect, and its exemptions are narrower. These are the cases that come up in a real audit.

  • Body copy, links, menus and button wordsApplies

    Everything a visitor reads at normal size needs 4.5:1. Links count twice: against the background here, and against the text around them under a separate rule, explained in use of colour.

  • Placeholder text, and text that appears on hover or focusApplies

    W3C names both. The pale "name@example.com" inside an empty field and the tooltip that appears under the pointer are text in the page, so they need the same ratio as a paragraph.

  • Text over a photo, video or gradientApplies

    The ratio is taken against the part of the picture behind the letters, and the part most like the text is the one that counts. W3C lists text over a busy background image as a known failure (F83).

  • Disabled controlsExempt

    A control that is visible but cannot be used, such as a Submit button that waits until the form is complete, has no contrast requirement. A pale button that still works is not inactive, so it needs the full ratio.

  • Logos and brand namesExempt

    Text that is part of a logo is free. Brand guidelines are not: W3C says other text that fails because of a corporate palette is not exempted, and a logo shown pale until the pointer reaches it is a design choice that fails.

  • Decoration and incidental textExempt

    Text that is pure decoration, that nobody can see, or that happens to be in a photo (a shop sign behind a person) has no requirement. A banner made as an image of a headline is not incidental; it is an image of text and must pass.

Takeaway

If a visitor is meant to read it, it needs 4.5:1, or 3:1 when it is really large. Only logos, decoration and controls that cannot be used are left out.

Controls, focus rings and graphs: the 3:1 rule

For a control, 1.4.11 asks about whatever tells a visitor that the control is there and what state it is in: the border of an empty text field, the box of a checkbox and the mark inside it, the thumb of a toggle, the icon on an icon-only button. Each needs 3:1 against the colours next to it. For a white field on a white page that is the border; W3C's own passing example is a #767676 border, the same grey this site draws its hairlines in.

What 1.4.11 does not ask

  • A button whose words already show it is a button needs no border at all. Its focus indicator still has to stand out 3:1 against the background it sits on, which is the subject of the focus indicator guide.
  • Controls left exactly as the browser draws them, and disabled controls, are exempt.
  • A hover colour does not need 3:1 against the resting colour. But a state shown by a change of colour alone, such as a selected tab that is only a different shade, is a separate failure under 1.4.1.

Graphs, charts and icons

In a chart, the parts a reader needs (each line, bar or slice, and the gridlines that let them read a value) need 3:1 against their background. Lines that rarely touch do not need to contrast with each other, but the slices of a pie do, because their edges carry the meaning, unless every slice is also labelled with its value in text. Text inside a chart is held to the text rule. Heat maps, flags and screenshots fall under the "essential" exception: changing their colours would change what they mean.

Takeaway

Measure every border, icon and chart line against the colour right next to it, at 3:1. Then check the focus ring the same way.

Line drawing of a faint and a solid button on a monitor, the solid one ringed in orange
Fig. 2A faint button and a solid one on the same screen, and only the solid one is easy to find

How the ratio is computed

Contrast ratio = (L1 + 0.05) / (L2 + 0.05), L1 = relative luminance of the lighter colour, L2 = the darker. Ratios run from 1:1 to 21:1. Relative luminance is the colour's brightness from 0 for black to 1 for white, worked out from its red, green and blue values, with green weighted heaviest.

The formula from the WCAG 2.2 text, as JavaScript

// WCAG 2.2 relative luminance and contrast ratio
// c8 is one 8-bit channel, 0 to 255
function channel(c8) {
  const c = c8 / 255;
  return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
}
// 0 for black, 1 for white
function luminance([r, g, b]) {
  return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);
}
// from 1 to 21, lighter colour on top
function contrast(a, b) {
  const [l1, l2] = [luminance(a), luminance(b)].sort((x, y) => y - x);
  return (l1 + 0.05) / (l2 + 0.05);
}

Scroll sideways to see every column.

Relative luminance of the grey #6B7280, one channel at a time
Channel (8-bit)÷ 255Linear× weight
Red 1070.41960.14700.0313
Green 1140.44710.16830.1203
Blue 1280.50200.21590.0156

Add the last column and the grey's relative luminance is 0.1672. White is 1, so on a white page the ratio is (1 + 0.05) / (0.1672 + 0.05) = 4.83:1, a pass for body text. Put the same grey on a light card of #F3F4F6 (luminance 0.9041) and it drops to 4.39:1, a fail. Nothing about the text changed; the card did.

Two details trip people up. The spec's threshold is 0.04045; before May 2021 it read 0.03928, and W3C says the change has no practical effect. Tools use 0.04045. And ratios are thresholds and are not rounded: 4.499:1 does not meet 4.5:1. A checker that shows "4.5:1" for 4.499 is showing you a pass that is not there.

Take colours from your stylesheet or theme settings, not from an eyedropper on a screenshot: anti-aliasing draws thin text fainter than its real colour, and W3C measures the colour you specified. If you set a text colour you must set the background too (and the other way round); W3C counts setting only one as a failure (F24), because a visitor's default background is unknown. The contrast check runs this formula on any two colours you give it.

Fails an auditor finds every week

WebAIM's 2026 scan of the top million home pages (results from February 2026) put low contrast text at the top of its list of the six most common failures, on 83.9% of home pages. These are the shapes it takes on a small-business site.

  • Grey text on a grey card

    The worked example above, in the wild. A theme grey that passes on the page background (#6B7280 on white, 4.83:1) fails once it sits on a light panel (4.39:1). Test text on every background it can land on, not only the page.

  • White words on a mid-tone brand colour

    Teals, greens, oranges and pinks in the middle of the range are the trap. White on a bright teal like #14B8A6 is 2.49:1: below even the large-text line, so the "Book now" button fails at any size.

  • A headline across a hero photo

    It passes over the dark trees and fails over the sky. The photo changes with every campaign, so a heading that passed in spring fails in summer unless something behind the words holds the contrast steady.

  • Charts in pale pastels

    Gridlines in #D1D5DB on white are 1.47:1, and pastel series colours often sit near that. If a reader needs the line to read the value, it needs 3:1.

  • Partner logos greyed out until hover

    A common "as seen in" strip. The logos are exempt as logos, but W3C says presenting them faded is an author choice, not brand law, so the faded state fails.

How to fix a fail without breaking the brand

A contrast fix rarely means a new palette. It means using each brand colour where it is strong enough and giving it a darker partner where it is not.

Darken the foreground, keep the hue

Step the colour toward black along the same hue until it clears the line. The bright teal above becomes #0F766E, and white on it is 5.47:1; it still reads as the same brand. Most brand systems end up with two shades of each colour: the light one for fills, large headings and borders, the dark one for text and for anything text sits on. On the grey card, moving the text from #6B7280 to #4B5563 gives 6.87:1.

Put a scrim behind text on images

A scrim is a translucent layer between the picture and the words. Laid over the whole picture, or only under the text, it caps how light the background can get. A 55% black layer turns even pure white into about #737373, and white text on that is 4.74:1, so the heading passes whatever photo goes in next month. W3C's own technique describes the same idea the other way round: fogging the picture white behind dark text.

A scrim in CSS: the photo stays, the words always pass

.hero { position: relative; }
/* the scrim */
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  background: rgb(0 0 0 / 0.55);
}
.hero h1, .hero p { position: relative; color: #fff; }

Try it: a scrim behind text on a picture

Spring tasting menu

Book a table for Friday or Saturday night.

Lightest point behind the words: #726F6A. White text on it is 5.00:1, which passes for the small line and the large heading.

The picture here is a gradient standing in for a photo. On a real photo, measure the lightest area the words cross.

A text outline or halo also works: W3C counts a narrow border around each letter as part of the letter, and a wide one as the background, so a dark halo behind light text can carry the ratio on its own.

Fix it once, where the colour is defined

Change the theme setting, the CSS variable or the design token, not each page. Then recheck the hover, focus and placeholder states that share it. The accessible colour palette check tests every pair in a brand palette at once and suggests the nearest shade that passes; on Shopify, the theme comparison has a colour-scheme check for the settings most themes expose.

A fix lives in your own code

A widget that recolours the page in the visitor's browser leaves your stylesheet failing. Our guide to accessibility overlays explains why that does not make a site conform.

How to test your own pages

  1. List every text colour and every background in the theme, including cards, banners and the footer.
  2. Check each pair that can meet on a page in the contrast check, then the hover, focus and placeholder versions of the same pairs.
  3. For text on photos, measure against the lightest area the words cross; if it moves with the photo, add a scrim.
  4. Measure input borders, icons, chart lines and the focus ring against the colour beside them, at 3:1.
  5. Run a scanner over the whole site for the flat-colour pairs you missed; most flag text on images for a person to judge rather than measuring it. The testing tools comparison lists the options, and contrast is one line of the WCAG 2.2 AA checklist.

Quick reference

  • 1.4.3 AA: 4.5:1 for text, 3:1 for text of 18pt, or 14pt bold, and up.
  • 1.4.11 AA: 3:1 for borders, icons, focus rings and chart parts against their neighbours.
  • 1.4.6 AAA, optional: 7:1 and 4.5:1.
  • Exempt: logos, decoration, disabled controls. Not exempt: brand colours, placeholders, text on photos.
  • Never round: 4.499:1 is a fail.