WCAG contrast checker

Test any text and background pairing against WCAG 2.2 AA and AAA, for both normal and large text, with a live preview of the result.

WCAG Colour Contrast Checker

Check if your text and background colour combination meets WCAG 2.2 accessibility standards for AA and AAA compliance.

12.63
: 1 Contrast Ratio
AA Normal Text (≥ 4.5:1) PASS ✓
AA Large Text (≥ 3:1) PASS ✓
AAA Normal Text (≥ 7:1) PASS ✓
AAA Large Text (≥ 4.5:1) PASS ✓

Live Text Preview

Heading Text Sample (24px)

This is what your body text will look like with these colour combinations. Check if it's easily readable at this size.

Small text (12px) is harder to read and needs higher contrast ratios to pass WCAG compliance.

How to use this tool

Contrast ratio compares the relative luminance — the perceived brightness — of two colours. It runs from 1:1 (identical, invisible) to 21:1 (pure black on pure white). The calculation is defined by WCAG and weights the red, green and blue channels differently, because human eyes are far more sensitive to green than to blue.

That weighting is why intuition misleads people. A mid-yellow and a mid-blue can look equally "medium" while having wildly different luminance, and two colours that feel high-contrast to you may fail outright. Measuring is not optional.

The four thresholds, and which one applies

AA is the level that legislation and procurement rules point to — the UK's public sector accessibility regulations, the European Accessibility Act (through the harmonised standard EN 301 549) and Section 508 in the US. It requires 4.5:1 for normal text and 3:1 for large text. AAA is the enhanced level, requiring 7:1 and 4.5:1 respectively.

"Large text" has a specific definition: at least 24px, or 18.66px if bold. It gets a lower threshold because thicker letterforms remain legible at lower contrast. Note that the requirement covers text and images of text — plus, since WCAG 2.1 and still in the current WCAG 2.2, a 3:1 minimum (success criterion 1.4.11) for meaningful UI components and graphics such as input borders, icons and focus indicators.

The mistakes that fail real audits

Three patterns account for most failures. Placeholder text is the worst offender — placeholder greys are pale by design, some browser defaults fall short, and many design systems lighten them further. Placeholders are frequently misused as labels, so the information disappears for low-vision users. Light grey body text on white is the second: it looks refined in a design mockup on a bright calibrated monitor, and becomes unreadable on a phone in daylight. Text over images or gradients is the third, because the ratio changes across the background — check the lightest and darkest points it crosses, not the average, and add a scrim if either end fails.

Also remember that disabled controls are exempt from the contrast requirement, but "looks disabled" is not the same as being disabled. If a control is interactive, it must meet the threshold.

Designing for contrast from the start

Retrofitting contrast is painful; building it in is nearly free. Generate your palette as a tint-and-shade scale and record which steps are text-safe on which backgrounds — as a rule of thumb, steps around 600 and darker work for text on white, and 200 and lighter work as backgrounds for dark text. Our palette generator produces exactly that kind of scale.

One caution about automated testing: tools like Lighthouse and axe catch a meaningful share of contrast problems, but they cannot evaluate text over images, canvas or video, and they miss states they never trigger — hover, focus, error. Manual spot-checks on those states remain necessary.

Picking black or white text automatically

If users can choose their own brand or theme colours, your code has to choose the text colour. The WCAG 2.2 formula is short, but copies of it around the web often break in two ways: the hex parser only understands six-digit colours, so #777 produces NaN, and the regex accepts characters that are not hex at all, which parseInt silently misreads. This version validates its input, expands #rgb shorthand, and uses the 0.04045 threshold from the current specification. Older copies use 0.03928; for 8-bit colours the result is identical, because no 8-bit channel value falls between the two.

function parseHex(hex) {
  const m = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i.exec(hex.trim());
  if (!m) throw new Error(`Not a hex colour: ${hex}`);
  const h = m[1].length === 3 ? m[1].replace(/./g, '$&$&') : m[1];
  return [0, 2, 4].map(i => parseInt(h.slice(i, i + 2), 16) / 255);
}

// WCAG 2.2 relative luminance (note the 0.04045 threshold)
function luminance(hex) {
  const [r, g, b] = parseHex(hex).map(c =>
    c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4);
  return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}

function contrast(a, b) {
  const [hi, lo] = [luminance(a), luminance(b)].sort((x, y) => y - x);
  return (hi + 0.05) / (lo + 0.05);
}

// Whichever of black or white contrasts more with the background
const textOn = bg =>
  contrast(bg, '#fff') >= contrast(bg, '#000') ? '#fff' : '#000';

console.log(contrast('#007bff', '#fff').toFixed(2)); // 3.98 – fails AA body text
console.log(contrast('#007bff', '#000').toFixed(2)); // 5.28 – passes
console.log(textOn('#007bff'), textOn('#333'));      // #000 #fff
console.log(contrast('#777', '#fff').toFixed(2));    // 4.48 – #rgb works

Two results surprise people. First, #007bff, a very common button blue, is more readable with black text than white: 5.28:1 against 3.98:1, and white fails AA for body text. Second, if you pick whichever of black or white scores higher, the result is never below 4.58:1 for any background colour, so the rule always passes AA for normal text. It does not guarantee AAA's 7:1. For that you have to adjust the background itself.

The maths is in the WCAG 2.2 definition of relative luminance, and the reasoning behind the thresholds is in the W3C's Understanding SC 1.4.3: Contrast (Minimum).

Frequently asked questions

What contrast ratio do I need to pass WCAG?

For AA — the level most laws reference — you need 4.5:1 for normal text and 3:1 for large text. For AAA it is 7:1 and 4.5:1. WCAG 2.2 also requires 3:1 for meaningful UI components such as input borders and focus indicators.

What counts as "large text" in WCAG?

Text of at least 24px, or 18.66px if bold. Larger and heavier letterforms stay legible at lower contrast, so they get the more relaxed 3:1 threshold instead of 4.5:1.

Does placeholder text need to pass contrast?

Yes. Placeholder text is real text and must meet the same 4.5:1 minimum. Pale placeholder grey is one of the most common contrast failures on the web — and placeholders should never replace a visible label.

How do I check contrast for text over an image?

Check the text against the lightest and the darkest point of the background it crosses, not an average. If either fails, add a semi-transparent scrim between the image and the text, or apply a text shadow, until both extremes pass.