ScreenshotNeo

BlogHow-to

CSS Color Contrast: How to Improve Accessibility

Learn WCAG 2.2 AA contrast thresholds, how to check text and interface colors, and a repeatable CSS workflow for improving accessibility.

By the ScreenshotNeo team4 October 20267 min read

To meet WCAG 2.2 Level AA contrast requirements, use a contrast ratio of at least 4.5:1 for normal text and 3:1 for large text. Check interface boundaries, state indicators, and meaningful graphics separately: applicable non-text visuals generally need 3:1 against adjacent colors. Then review the rendered page, because a passing ratio alone does not guarantee comfortable reading in every font or context.

This guide shows how to identify the color pairs that matter, calculate and improve them in CSS, check non-text visuals, and troubleshoot common failures.

1. Know which WCAG contrast threshold applies

What you are checking WCAG 2.2 criterion AA threshold
Normal text and images of text SC 1.4.3 Contrast (Minimum) 4.5:1
Large text and images of large text SC 1.4.3 Contrast (Minimum) 3:1
Specified UI component and state visuals, and relevant graphical objects SC 1.4.11 Non-text Contrast Generally 3:1 against adjacent color(s)

For SC 1.4.3, large text means at least 18 point regular or 14 point bold. Those sizes are approximately 24 CSS pixels and 18.5 CSS pixels, respectively. Use the actual size and weight rather than judging by appearance alone. Images of text are covered too, subject to the criterion’s exceptions. The ratio is a threshold: 4.499:1 does not round up to a pass. W3C: Understanding SC 1.4.3.

SC 1.4.3 has exceptions for text that is part of an inactive UI component, purely decorative, not visible to anyone, part of a picture containing significant other visual content, or part of a logo or brand name. Do not use these exceptions to excuse ordinary content text.

For a more demanding Level AAA target, SC 1.4.6 specifies 7:1 for normal text and 4.5:1 for large text. This is optional beyond AA. W3C: Understanding SC 1.4.6.

Non-text contrast is a separate check. A button label can pass its text threshold while the button boundary or selected-state indicator fails its applicable check. SC 1.4.11 generally requires 3:1 for specified visual information needed to identify UI components and states, and for graphical objects needed to understand content, against adjacent colors, subject to its exceptions. W3C: Understanding SC 1.4.11.

2. Find the color pairs users actually see

Contrast applies to foreground and background colors that appear together in typical presentation. Inventory more than the default body copy. Include headings, links, buttons, form fields, placeholders, error and success text, focus indicators, selected states, and meaningful icons. Check hover and disabled states where the criterion applies.

  • Write down each foreground/background pair and where it appears.
  • Identify text size and weight so you choose the correct text threshold.
  • For controls and graphics, note the adjacent colors and the visual information needed to identify the component or state.
  • Check overlays and translucent surfaces in their rendered context; the visible background affects the resulting pair.

Do not let color alone identify a link, error, or state. Add another cue where needed, such as an underline, icon, text label, or shape change. WCAG SC 1.4.1 addresses use of color. W3C: Understanding SC 1.4.1.

3. A repeatable CSS check-and-adjust workflow

  1. Choose the criterion. For text, classify it as normal or large using the defined point size and weight. For a UI boundary, state indicator, or meaningful graphic, assess SC 1.4.11 separately.
  2. Measure the actual pair. Use a contrast checker to evaluate the foreground and background colors as rendered. W3C Design System guidance points authors to contrast-checking tools, but there is no single checker established here as best. W3C Design System: Colour.
  3. Adjust a color and recheck. If a pair fails, change the text or surface color, then measure the resulting pair again. Where the design permits, leave some headroom above the threshold, especially for small or thin text. That margin is a practical design choice, not an extra WCAG requirement.
  4. Specify both sides of the pair. Set foreground and background colors where needed so user-agent defaults do not produce an unexpected combination. W3C identifies specifying only one side of a foreground/background pairing as a common failure.
  5. Review the rendered result. Look at the actual typeface, weight, size, and surrounding UI. Thin or unusual fonts may look fainter because of antialiasing even when the numeric ratio passes.
  6. Repeat across states and themes. Recheck focus, hover, validation, selected states, and dark theme if supported. A passing default state does not establish that every other color pair passes.

CSS example: use tokens for pairs you can recheck

:root {
  --page-bg: #ffffff;
  --body-text: #222222;
  --link-text: #0645ad;
  --button-bg: #123b62;
  --button-text: #ffffff;
  --focus-ring: #8a2be2;
}

body {
  color: var(--body-text);
  background-color: var(--page-bg);
}

a {
  color: var(--link-text);
  text-decoration: underline;
}

.button {
  color: var(--button-text);
  background-color: var(--button-bg);
}

:focus-visible {
  outline: 3px solid var(--focus-ring);
  outline-offset: 2px;
}

These declarations illustrate a maintainable setup, not a claim that every pair in the example passes. Verify each pair in a contrast checker and in the rendered design. Focus outlines, button boundaries, and other non-text visuals need their own applicable review.

4. Check non-text visuals and real rendering

Separate text from the visuals that make a control or state recognizable. For example, check a button label against its fill under SC 1.4.3, then assess any necessary boundary or state indicator under SC 1.4.11. Meaningful chart marks, icons, and selected-state visuals may also need a separate check when their appearance conveys information.

Review the actual typeface and rendering. A numeric pass is not a guarantee of comfortable reading: thin or unusual fonts can appear fainter because of antialiasing. Also verify that users can distinguish meaning without color alone—for example, a link can be underlined, and an error can include explanatory text or an icon as well as color.

5. Or skip the browser setup

If you also need screenshots of a page while reviewing rendered contrast, ScreenshotNeo can capture a URL in one GET request. Its capture process accepts cookie and consent banners like a visitor and removes more than 60 known consent platforms, newsletter popups, and chat widgets before the shot; each step can be turned off. Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers say which outcome occurred. Its MCP server provides screenshot, page-info, and PDF tools for AI agents.

See the ScreenshotNeo API documentation for options and setup. Example request, saving the returned image as a file:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp

1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000. Learn about ScreenshotNeo or sign up for free.

6. Troubleshooting contrast issues

Symptom Likely cause What to do
Text fails at 4.5:1 The pair is below the normal-text AA threshold. Adjust the foreground or background color and recheck the pair. Do not classify text as large unless it meets the defined size and weight.
A result shows 4.499:1 and is treated as close enough The threshold was rounded for interpretation. Treat it as a failure. The ratio is not rounded up to pass.
Text passes in a checker but appears faint Thin or unusual type, antialiasing, small sizing, or its surrounding presentation may affect perceived legibility. Review the rendered font and context; consider a stronger weight or colors farther from the threshold, then recheck.
Button text passes, but the control is hard to locate The text pair was checked, but the control boundary or state indicator was not. Assess the relevant non-text visual separately against adjacent colors under SC 1.4.11 where applicable.
Links or errors are unclear in grayscale or without color perception Color is carrying meaning by itself. Add another cue, such as an underline, icon, label, or shape change, and review SC 1.4.1.
A color pair changes between browsers or themes One side of the pair is unspecified, or the visible background differs. Specify foreground and background where needed and check each rendered theme and relevant state.

7. Performance, reliability, and maintenance

Contrast checks are inexpensive design-time reviews compared with discovering inconsistent colors after a component is reused across many pages. Centralize colors in CSS custom properties or design tokens, then review changed pairs whenever a token, theme, component state, or type style changes. Keep a short record of which pairs and states were checked so future edits can be repeated.

For reliable results, evaluate the colors users actually see, including composited backgrounds and state changes, and use a checker as an aid rather than a substitute for rendered review. A passing contrast ratio addresses a specific criterion; it does not guarantee comfortable reading in every font, display, or user context, and it does not establish conformance with unrelated accessibility criteria.

Frequently asked questions

Does a passing contrast ratio mean the page is fully accessible?

No. It evaluates a particular color pair against a particular contrast criterion. Other accessibility requirements and usability factors still need review.

Is 4.5:1 required for every text size?

For WCAG 2.2 AA SC 1.4.3, normal text needs 4.5:1; qualifying large text needs 3:1. Use the criterion’s size and weight definitions.

Should I target AAA instead of AA?

SC 1.4.6 defines the Level AAA thresholds as 7:1 for normal text and 4.5:1 for large text. Whether to adopt AAA is a project decision; AA and AAA thresholds should not be conflated.

Can a contrast checker tell me whether a font feels readable?

A ratio checker evaluates colors. It cannot by itself establish how comfortable the rendered typeface, weight, size, and surrounding design will be to read.