12k
All articles

How to Check Color Contrast for WCAG

Check WCAG color contrast ratios for text, UI components, focus indicators, and links with the right tools, thresholds, and fixes.

OpenReplay Team
OpenReplay Team
How to Check Color Contrast for WCAG

To check color contrast for WCAG, compare the relative luminance of your foreground text and its background, then confirm the resulting ratio meets the threshold for that element type: at Level AA, that’s 4.5:1 for normal text, 3:1 for large text, and 3:1 for non-text UI.

Most of us learn this the hard way, shipping a light grey label that looked fine on our own screen and failed the audit two sprints later. You don’t calculate this by hand: a checker does it from two hex values in seconds. This guide gives you the exact thresholds, the fastest ways to measure a ratio, the failures that trip up most teams, and the rules that reach past body text into UI components, focus indicators, and color-coded meaning.

Key Takeaways

  • WCAG Level AA requires 4.5:1 contrast for normal text and 3:1 for large text; Level AAA raises those to 7:1 and 4.5:1.
  • Non-text elements (input borders, button outlines, meaningful icons, chart segments, and focus indicators) need 3:1 against adjacent colors under WCAG 1.4.11.
  • “Large text” is 18pt (about 24px), or 14pt bold (about 18.66px) and larger; anything smaller is held to the 4.5:1 normal-text threshold.
  • The most common failure is light-gray body text: #999999 on white scores about 2.85:1 and fails, while #595959 reaches 7:1 and passes AA and AAA.
  • WCAG 2.2 is the current standard, but its contrast minimums are identical to 2.1 and 2.0. The numbers have not changed.

What contrast ratio does WCAG require?

Contrast ratio measures the luminance difference between two colors. The floor is 1:1, which is what you get when foreground and background are the same color, and the ceiling is 21:1, black against white. WCAG computes it as (L1 + 0.05) / (L2 + 0.05), where L1 and L2 are the relative luminance of the lighter and darker colors. Here are the thresholds every checker enforces:

ElementLevel AALevel AAASuccess criterion
Normal text4.5:17:11.4.3 / 1.4.6
Large text3:14.5:11.4.3 / 1.4.6
Non-text (UI, graphics, focus)3:11.4.11

At Level AA that means 4.5:1 for normal text and 3:1 for large text; Level AAA lifts the same pair to 7:1 and 4.5:1. What counts as large is a question of size and weight: 18pt (about 24px) and up, or 14pt (about 18.66px) and up if the text is bold. The WebAIM Contrast Checker reports against exactly those thresholds. One decision cue covers most cases: is the text large? → 3:1; otherwise → 4.5:1.

A myth worth killing: WCAG 2.2, published in October 2023, added focus-appearance guidance but left the contrast minimums untouched. Later versions only add success criteria rather than rewrite the ones already in place, with 4.1.1 Parsing the single exception, so 4.5:1 / 3:1 / 7:1 have held steady across 2.0, 2.1, and 2.2. Low contrast remains the web’s most common accessibility defect. The 2026 WebAIM Million report found low-contrast text on 83.9% of the top million home pages.

How do you check color contrast?

The fastest workflow is: get the two rendered hex values, paste them into a checker, read the pass/fail. Grab the rendered color, not the value in your design file: overlays, gradients, and transparency change what the user actually sees.

  • WebAIM Contrast Checker: paste foreground and background hex. You get the ratio plus five verdicts, since normal text and large text are each judged at AA and AAA, and non-text elements get their own AA line. If the pair falls short, the Lightness sliders let you nudge either color until it clears, as WebAIM’s walkthrough of the tool shows.
  • TPGi Colour Contrast Analyser: a desktop app for Windows and macOS. Pick either color straight off the screen with its eyedroppers, type values in hex, RGB, HSV, or HSL, set an alpha value on the foreground, and drag the sliders until a failing pair passes. It also previews your colors through eight vision-deficiency settings.
  • Chrome DevTools: inspect the element, open the Color Picker from the swatch beside its color declaration, and expand the Contrast ratio section. It reports whether the pair clears AA and AAA, offers a Use suggested color button that applies a passing value, and draws the AA and AAA cut-offs as lines in the shades preview so you can drag the picker below them by hand.
  • Firefox: open DevTools → the Accessibility panel → select a node → read its contrast and pass/fail.

For whole-page audits, run WAVE, axe DevTools, or Lighthouse. WAVE reads the text and background colors in your styles and catches most body text sitting under the AA 4.5:1 line, applying the lower 3:1 threshold to large text. What it cannot judge is text over images, alpha compositing, or hover and focus states, so verify important components by hand.

Common Contrast Failures and How to Fix Them

Most failures are a handful of repeat offenders with quick, verifiable fixes.

Light-gray text on white. #767676 on white is the lightest gray that still clears AA at 4.5:1, while #999 manages only 2.85:1 and fails. Darken body text to #595959, which lands on exactly 7:1 and clears both AA and AAA.

/* FAIL: ~2.85:1 */  color: #999999; background: #fff;
/* PASS: 7:1 */      color: #595959; background: #fff;

Placeholder text. Setting placeholders to 40–50% opacity is the usual cause of a 1.4.3 failure, because the background bleeds through and drags the ratio under 4.5:1. Give them a real hex value of #767676 or darker instead. And use a persistent <label> for anything the user must read, since placeholders vanish on input.

Links by color alone. Add text-decoration: underline so the link isn’t distinguished by hue only. Strip the underline and you take on an extra requirement: the link text then needs 3:1 against the surrounding body text, on top of the 4.5:1 that both colors owe the background.

Text over images. Luminance varies across a photo, so the same text passes in one area and fails in another. Add a semi-transparent scrim:

.hero {
  background:
    linear-gradient(rgba(0,0,0,.6), rgba(0,0,0,.6)),
    url("hero.jpg");
}
.hero-text { color: #fff; } /* now has guaranteed contrast */

Beyond Text: UI Components, Focus, and Use of Color

Contrast rules extend past paragraphs. Success criterion 1.4.11 sets a 3:1 floor for controls and for the parts of a graphic a reader needs in order to follow the content, measured against whatever color sits next to them. Input borders, button outlines, icons that carry meaning, and chart series all qualify. States count too, with a caveat: the default state needs 3:1 and no state may drop the component below it, but a hover effect you add on top isn’t itself held to 3:1, as long as it doesn’t erase the contrast the control already had.

Focus indicators are where the spec numbers get muddled, so map them precisely. Focus visibility is 2.4.7 Focus Visible. The 3:1 contrast for the indicator comes from 1.4.11 Non-text Contrast, not from 2.4.11, which is Focus Not Obscured (Minimum) and concerns overlays hiding a focused element. The AAA-level 2.4.13 Focus Appearance adds minimum size and state-change contrast on top.

Finally, WCAG 1.4.1 Use of Color (Level A) means color alone must never carry meaning: pair a red/green status with a text label or icon, and give links an underline. One exemption to remember: inactive (disabled) components are excluded from the non-text and contrast minimums, but keeping them readable is still better UX than graying them into invisibility.

Bake Contrast Into Your Workflow

Catch contrast before it ships. Check during design with a plugin like Stark inside Figma, then encode the results in a token layer that annotates each color’s ratio:

:root {
  --text-primary:   #171717; /* 18.8:1 — headings, body */
  --text-secondary: #595959; /* 7:1  — secondary text */
  --border-input:   #767676; /* 4.5:1 — meets 1.4.11 */
  --border-subtle:  #d4d4d4; /* 1.6:1 — decorative only */
}

Body text should sit well above the minimum (10:1+ is comfortable), while secondary and large text can hold at 4.5:1 and borders at 3:1. Test for color blindness in Chrome DevTools → Rendering → Emulate vision deficiencies to confirm meaning survives without color. Then automate: run axe or Lighthouse in CI so a contrast regression fails the build instead of reaching users. And give dark mode its own pass: inverting luminance breaks ratios that passed in light mode, so recalculate every token per theme.

Contrast is one of the cheapest accessibility problems to fix and one of the easiest to prevent. Pick a checker, set your thresholds as tokens, wire an automated scan into CI, and the failures above stop shipping. Start by auditing your current body text and focus styles against the 4.5:1 and 3:1 lines today.

FAQs

Does WCAG contrast measure hue and saturation, or only lightness?

Contrast ratio measures only relative luminance, the perceived brightness of each color, not hue or saturation. This is why two colors that look different, like red text on a green background, can still fail if their luminance is close. The ratio is computed as (L1 + 0.05) / (L2 + 0.05) from the lighter and darker luminance values, so meaning conveyed by hue alone is separately governed by WCAG 1.4.1 Use of Color.

What is the difference between WCAG 1.4.11 Non-text Contrast and 2.4.11 Focus Not Obscured?

WCAG 1.4.11 Non-text Contrast (Level AA) requires a 3:1 contrast ratio for UI components and meaningful graphics, including the focus indicator, against adjacent colors. WCAG 2.4.11 Focus Not Obscured (Minimum, Level AA), new in WCAG 2.2, is unrelated to contrast: it requires that a focused element is not entirely hidden by author-created content such as sticky headers or overlays. Contrast is 1.4.11; visibility from obstruction is 2.4.11.

Why does the same text pass contrast in my design file but fail in the browser?

Design-file plugins check the flat color values you assign, but browsers composite the rendered result. Opacity, semi-transparent overlays, gradients, background images, and blend modes all change the actual luminance a user sees. A placeholder set to 50 percent opacity or text over a photo can pass in the mockup and fail once rendered. Always sample the rendered hex from DevTools or an on-screen eyedropper tool rather than trusting the source color.

Do disabled buttons and inactive controls need to meet WCAG contrast minimums?

No. WCAG 1.4.3 Contrast (Minimum) explicitly exempts inactive user interface components, and disabled elements are also excluded from the 1.4.11 non-text contrast requirement. That means a grayed-out disabled button will not fail an automated contrast audit. Keeping disabled controls legible is still better usability, since fully invisible controls confuse users, but it is not a WCAG conformance requirement for that state.

Digital experience platform

Truly understand users experience

See every user interaction, feel every frustration and track all hesitations with OpenReplay — the open-source digital experience platform. It can be self-hosted in minutes, giving you complete control over your customer data.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.