Home › Color contrast checker

Color contrast checker for WCAG 2.2

Exact ratio, AA and AAA pass or fail, a live preview, and a matrix that tests every pair in your palette.

Contrast checker

21.00:1

Excellent. Passes every WCAG level, including AAA for normal text.

    Large heading text (24px bold)
    Body copy at 16px, the size most paragraphs are set in.
    Small print at 13px: captions, timestamps, helper text.
    Same pair, inverted
    Background as text, text as background. The ratio is identical.
    Useful for dark mode and for buttons with light labels.

    Palette mode: every pair at once

    Paste the colors from your design tokens (up to 8). The matrix shows the ratio for every text and background combination, marked AAA, AA, Headings only, or Fail. This is the fastest way to find out which of your greys can carry body text and which are decoration only.

    The single-pair checker above also gives you a link you can paste into a ticket (the URL updates as you type) and, when a pair fails AA, the closest text color that passes. Nothing is uploaded: the math runs in your browser.

    Want this checked on every page, every week? AdaClear Starter is €19 a month: weekly scans of up to 150 pages on one site, an email when a new issue appears, and a dated PDF report you can hand to a client or an enterprise buyer.

    How the contrast ratio is computed

    WCAG does not compare colors by how far apart they look. It compares how much light each color reflects, a value called relative luminance. Every color is turned into a number between 0 (pure black) and 1 (pure white), and the ratio is the brighter number divided by the darker one, with a small constant added to both so that black on black does not divide by zero.

    The steps, for a color given as sRGB channels R, G and B in the range 0 to 255:

    1. Divide each channel by 255 so it sits between 0 and 1.
    2. Linearize each channel. If the value is 0.04045 or lower, divide it by 12.92. Otherwise compute ((value + 0.055) / 1.055) raised to the power 2.4. This step undoes the gamma curve that screens apply.
    3. Weight the channels for how sensitive the eye is to them: L = 0.2126 R + 0.7152 G + 0.0722 B. Green dominates, which is why a mid green looks brighter than a mid blue at the same numeric value.
    4. Ratio = (L1 + 0.05) / (L2 + 0.05), where L1 is the lighter luminance and L2 the darker one.
    function luminance(r, g, b) {
      const lin = c => {
        c = c / 255;
        return c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
      };
      return 0.2126 * lin(r) + 0.7152 * lin(g) + 0.0722 * lin(b);
    }
    function ratio(a, b) {
      const [l1, l2] = [luminance(...a), luminance(...b)].sort((x, y) => y - x);
      return (l1 + 0.05) / (l2 + 0.05);
    }

    The result ranges from 1:1 (identical colors) to 21:1 (black on white). The checker above rounds to two decimals, the same precision browser developer tools use. Two things follow from the formula that surprise people: swapping text and background does not change the ratio, and the ratio is not linear. Going from 4.4:1 to 4.5:1 is a tiny visual change but the difference between a failed and a passed audit.

    What WCAG 2.2 actually requires

    Three success criteria cover contrast. They are unchanged between WCAG 2.1 and 2.2, so anything you fix today stays fixed under the version the ADA Title II rule and the European Accessibility Act reference.

    CriterionLevelWhat it coversMinimum ratio
    1.4.3 Contrast (Minimum)AANormal text4.5:1
    1.4.3 Contrast (Minimum)AALarge text: at least 18pt (24px), or 14pt (about 18.7px) if bold3:1
    1.4.6 Contrast (Enhanced)AAANormal text7:1
    1.4.6 Contrast (Enhanced)AAALarge text4.5:1
    1.4.11 Non-text ContrastAAUI component boundaries and states, focus indicators, and the parts of icons or charts needed to understand them3:1

    Three exemptions are written into 1.4.3 and worth knowing. Text that is part of a logo or brand name has no contrast requirement. Text in an inactive (disabled) control has no requirement. And purely decorative text, or text that is not visible to anyone, is exempt. Everything else, including placeholder text, link text, table captions and the labels inside buttons, must pass.

    Most organizations target AA. AAA is the right target for reading-heavy products (documentation, news, education) and for any audience that skews older, because contrast sensitivity drops measurably with age. A practical middle path: hit AAA for body copy and AA everywhere else.

    The failures we see on almost every site

    Grey helper text on white

    The single most common failure. Designers reach for #999999 or #a0a0a0 for captions, timestamps and secondary labels because it looks quiet. #999999 on white is 2.85:1, well short of 4.5:1. The lightest neutral grey that passes on a white background is #767676 (4.54:1). #777777 is 4.48:1 and fails, which is why so many audits flag it.

    Placeholder text

    Browser default placeholder styling is light grey and fails in every major browser. Placeholders are real text under WCAG, so either darken them with the ::placeholder selector or, better, stop relying on placeholders to label fields and use a visible label instead.

    "Disabled-looking" states that are not disabled

    The disabled exemption applies only to controls that are actually disabled. A muted-looking "secondary" button that still works must pass 4.5:1 for its label and 3:1 for its border. Faded rows in a table, greyed-out prices and "coming soon" tags are all live text.

    Focus rings

    Under 1.4.11 the focus indicator has to reach 3:1 against the colors next to it. A thin light-blue outline on a white card, or a brand-teal ring on a teal button, fails. Use a two-tone ring (a dark line inside a light one) or choose a ring color that contrasts with every background it can land on.

    Text on images and gradients

    Hero text over a photo passes in one corner and fails in another, and the checker can only test one pair at a time. Add a solid or semi-opaque scrim behind the text and test against the scrim color, not the photo.

    Brand colors as button backgrounds

    Many brand palettes were chosen for print or for a logo, not for white text. A light teal, orange or yellow button with white text usually fails. Either darken the button color (AdaClear's own CTA is #0b5e56, 7.64:1 with white) or switch to dark text on the light brand color.

    Dark mode as an afterthought

    Flipping a light theme by inverting colors keeps ratios only if every pair is inverted together. Mixed themes, where a light-mode grey is reused on a dark surface, are where most dark-mode failures come from. Run your dark palette through the matrix separately.

    How to fix contrast in CSS without redesigning

    Fix the token, not the element. Almost every failing color on a modern site comes from a handful of variables, so change them once and the whole site moves.

    :root {
      --text-muted: #6b7280;   /* 4.83:1 on white: passes AA */
      --text-faint: #767676;   /* 4.54:1 on white: the floor  */
    }
    ::placeholder { color: var(--text-muted); opacity: 1; } /* Firefox lowers opacity by default */
    button.secondary { color: #0b1220; background: #e2e8f0; border: 1px solid #94a3b8; }
    :focus-visible { outline: 3px solid #0b1220; box-shadow: 0 0 0 5px #ffffff; }

    Two more habits pay off. Keep text opacity at 1 and adjust the color instead, because opacity makes the effective color depend on whatever is underneath. And when a color must stay for brand reasons, change the text size or weight: at 24px, or 18.7px bold, the threshold drops from 4.5:1 to 3:1, which rescues many headings.

    Going from one pair to the whole site

    This checker answers "is this pair OK". It cannot tell you where that pair is used, how many elements fail today, or whether next week's deploy reintroduced a failing grey. The AdaClear site scan runs axe-core in a real browser against your live pages, lists every element that fails 1.4.3 and 1.4.11 with the exact foreground and background colors it measured, and then re-checks on a schedule so regressions get caught before a complaint does. Contrast is one item on the broader WCAG 2.2 checklist; the other high-volume automated finding is image alt text, which has its own free checker, and PDFs linked from your pages have the free PDF accessibility checker.

    Real world example: we audited every theme in the Shopify Theme Store and measured the contrast of each add-to-cart button. 29 of 302 buy buttons fail WCAG AA, some as low as 2.05:1. Read the Shopify theme accessibility report.

    Find every contrast failure on your site

    Free 30-second scan of any page: score, the elements that fail, and the colors involved. No overlay, no signup.

    Scan my site free →

    Frequently asked questions

    What counts as "large text"?

    At least 18 point (24 CSS pixels) at normal weight, or at least 14 point (about 18.66 CSS pixels) at bold weight (700 or heavier). Large text needs 3:1 at AA and 4.5:1 at AAA. Note that the point-to-pixel conversion assumes the default 96 dpi; if your base font size is unusual, measure the rendered size.

    Does the checker handle rgb() and short hex values?

    Yes. It accepts #abc, #aabbcc, aabbcc without the hash, and rgb(r, g, b) or rgba(r, g, b, a). Alpha is ignored because WCAG measures the color as rendered; if your text is semi-transparent, enter the color you see after blending, or set opacity to 1 and pick a solid color instead.

    Why does my design tool report a different ratio?

    Some tools round differently, some blend against a default background you did not choose, and a few use the newer APCA algorithm rather than the WCAG 2 formula. WCAG 2.2 conformance is measured with the formula on this page. APCA is a research candidate for a future WCAG 3 and is not the legal or contractual reference today.

    Do icons, charts and borders really need to pass?

    Under 1.4.11 the parts you need to perceive in order to understand or operate them do. An input's border, a checkbox outline, the selected state of a tab, the slices of a pie chart that carry meaning: all 3:1 against adjacent colors. Purely decorative graphics and disabled controls are exempt.

    Is 4.5:1 enough for people with low vision?

    It is the legal floor, not a comfort target. WCAG's own enhanced level is 7:1 for a reason: contrast sensitivity falls with age and with common conditions such as cataracts and diabetic retinopathy. If you can reach 7:1 on body copy without hurting the design, do it.

    Is anything I enter sent to AdaClear?

    No. The math runs in your browser and nothing is posted to a server. The page URL changes so you can share the pair; that is the only side effect. The full site scan is different: it fetches and renders the URL you give it, and the results are shown to you and not stored.