A11yScope

WCAG color contrast checker — and why your result is often wrong

The formula is the easy part. Most wrong contrast answers come from measuring against the wrong background.

Last reviewed 30 September 2026 · WCAG 2.2 · Runs entirely in your browser

Check a pair of colors

7.00:1

    Accepts #rgb, #rrggbb and rgb(…). Colors are treated as opaque — if yours has transparency, read the first trap below before trusting the number.

    What WCAG requires

    Contrast requirements in WCAG 2.2
    CriterionLevelNormal textLarge text
    1.4.3 Contrast (Minimum)AA4.5:13:1
    1.4.6 Contrast (Enhanced)AAA7:14.5:1
    1.4.11 Non-text ContrastAA3:1 for UI components and meaningful graphics

    Large text means at least 18 point (24 CSS pixels), or at least 14 point bold (about 18.66 CSS pixels at weight 700). Below that, it is normal text and needs 4.5:1 at AA.

    The ratio itself comes from relative luminance: (L1 + 0.05) / (L2 + 0.05), lighter over darker. It runs from 1:1 (identical) to 21:1 (black on white).

    Four ways contrast gets measured wrongly

    We learned each of these by getting it wrong in our own checker and noticing only because it flagged sites whose accessibility we had every reason to trust.

    1. Translucent backgrounds, read one layer deep

    getComputedStyle(el).backgroundColor on most text elements returns rgba(0, 0, 0, 0) — transparent. The color you see comes from an ancestor, and often from several stacked translucent layers. A card at rgba(255,255,255,0.8) over a dark hero is not white.

    The correct approach is to walk up the ancestors, collect every layer until one is opaque, and alpha-composite them from the bottom up. If nothing opaque is found, the canvas behind it all is white by default. Semi-transparent text needs the same treatment against the result.

    2. Gradients, either ignored or guessed

    A gradient background has no single color, so many tools give up and report "cannot measure" for every word on a gradient header. That is honest but noisy.

    There is a better answer that is still honest. The color stops are in the computed value. If the text clears the requirement against every stop, it clears it everywhere along the gradient, whatever the geometry — that is a proof, not an estimate. Only when a stop fails does it genuinely depend on where the text sits, and only then does it need a person to look.

    3. Text that is never painted

    This is the standard way to give an icon button a screen reader label:

    .icon-button {
      text-indent: -5000px;
      overflow: hidden;
    }

    The text is in the DOM; its color has a contrast ratio; and it is irrelevant, because nobody can see it. A checker that reads the element's box will measure it anyway. Measuring the text node itself with a Range, and checking that its rectangle survives every clipping ancestor, avoids reporting a problem that does not exist.

    The mirror image deserves care too: near-white text computed against a light background almost always means the real dark backdrop is a positioned sibling or a ::before — not an ancestor at all. "1.1:1" there is a confident wrong answer. The honest one is "could not see the background; check by eye".

    4. Placeholders, skipped

    Grey placeholder text is one of the most common real failures, and it is often skipped on the assumption that its color cannot be read. In Chrome it can: getComputedStyle(input, "::placeholder") returns it, including the browser's own default when the page sets none. Chrome's default placeholder grey clears 4.5:1 on white; the custom lighter greys designers reach for usually do not.

    A placeholder is not a label either. It disappears when the user starts typing. Passing contrast does not make a placeholder-only field accessible — WCAG 1.3.1 and 4.1.2 still need a real label.

    Checking a whole page

    A calculator answers one pair at a time. For every text element on a page, A11yScope — a free Chrome extension — composites translucent layers, measures gradients stop by stop, skips text that is never painted, and measures placeholders, then shows each failure with its measured ratio and both colors. What it cannot determine, such as text over a photograph, it reports for review rather than guessing.

    Sources