ScreenshotNeo

BlogGuides

Accessible Color Contrast: Choosing Colors for Websites

Learn how to choose and check accessible website colors against WCAG contrast thresholds, then review controls, links, and color-coded states.

By the ScreenshotNeo team4 October 20267 min read

To choose accessible website colors, check each foreground and background pair as it will actually appear. For WCAG 2.2 Level AA, normal text needs a contrast ratio of at least 4.5:1; large text needs at least 3:1. Then review meaningful controls and graphics separately, and make sure color is not the only cue that communicates a link, error, success state, or other meaning.

These ratios are conformance thresholds, not a complete measure of visual comfort or a guarantee that every passing combination will look equally readable. Leave some margin above the minimum where you can, and review the rendered design, especially when using thin or unusual fonts.

1. Understand the WCAG text thresholds

Text category WCAG 2.2 AA minimum Definition
Normal text 4.5:1 Text that does not qualify as large
Large text 3:1 At least 18 point regular, or at least 14 point bold

The size definition is in points. Do not assume a heading qualifies as large just because it looks prominent in a particular layout. When working in CSS pixels, account for the rendered size and font weight rather than relying on a visual guess.

The boundary is strict: a computed ratio of 4.499:1 does not meet a 4.5:1 requirement. Do not round a failing result up. The [W3C explanation of WCAG SC 1.4.3](https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html) covers the thresholds, text-size definition, exceptions, and calculation cautions. If you need to include AAA criteria in a design target, WebAIM describes 7:1 for normal text and 4.5:1 for large text as AAA thresholds; these are not the AA requirements.

SC 1.4.3 includes exceptions, such as inactive interface components, pure decoration, text not visible to anyone, certain text in images with significant other visual content, and logotype text. Apply an exception only when the specific content fits it. Do not assume meaningful interface content or every interactive logo is exempt.

2. Check every actual text and background pair

  1. List the pairs. Start with design tokens and include body copy, headings, labels, placeholders, buttons, navigation, error messages, and text over images or gradients.
  2. Use the intended state. Check the foreground against the background that appears in the real component, including overlays, hover and focus styling, and theme variants.
  3. Check the right threshold. Use 4.5:1 for normal text and 3:1 only when the text meets the large-text definition.
  4. Adjust a color and check again. Darkening the foreground or lightening the background often helps, but verify the resulting pair rather than relying on intuition about hue.
  5. Leave a practical margin. A pair just over the threshold can become a failure after a small palette change or a different rendering context.

Contrast is based on light-dark luminance, not simply how different two hues look. A colorful pair can still have too little luminance contrast. Use a checker for candidate values and sample the rendered page to catch differences between the token and what users see.

3. Evaluate controls and graphics too

Passing text checks does not finish the color review. WCAG SC 1.4.11 has separate non-text contrast requirements for applicable user-interface components and graphical objects. The minimum is 3:1 for the relevant comparisons. Whether a particular visual needs checking depends on the information it conveys.

  • Check visible control boundaries or indicators when users need them to identify a control or its state.
  • Check meaningful parts of diagrams and graphics when their visual information is needed to understand the content.
  • Review selected, checked, invalid, and focus states where visual indicators communicate state or identify an interactive element.

See the [W3C guidance for SC 1.4.11](https://www.w3.org/WAI/WCAG21/understanding/non-text-contrast.html) for how the criterion applies to interface components and graphical objects. It is a separate check from text-to-background contrast.

4. Do not use color as the only cue

A red error and a green success state should have another usable distinction, such as a text label, icon with an accessible name, shape, or pattern. Links should likewise be distinguishable without color alone. An underline is a clear default cue; the [W3C guidance on use of color](https://www.w3.org/WAI/WCAG21/understanding/use-of-color.html) explains the requirement to provide another visual way to identify information or distinctions.

Keep the two link checks separate: the link text still needs sufficient contrast against its background, and a link identified only by color needs to be distinguishable from surrounding text as well. WebAIM recommends underlining links by default; its [link contrast guidance](https://webaim.org/resources/linkcontrastchecker/) describes a 3:1 contrast relationship against surrounding text when color alone distinguishes the link, plus a non-color indicator on hover and keyboard focus.

5. Use a contrast checker and review the rendered page

The [WebAIM Contrast Checker](https://webaim.org/resources/contrastchecker/) accepts foreground and background colors and reports results for normal text, large text, and graphical objects or interface components. Its page also describes an eyedropper for sampling on-screen colors, a bookmarklet for checking page content, and a basic API. Section508.gov also points to contrast-checking resources and demonstrates adjusting a color and rechecking it in its guide to [accessible color usage](https://www.section508.gov/create/making-color-usage-accessible/).

A checker helps answer whether a pair passes a ratio threshold. A pair check or page sampling is not proof that the whole site has been audited.

A practical review checklist

  • Record relevant foreground and background pairs from the design tokens.
  • Check each text pair against the threshold for its actual size and weight.
  • Adjust failing colors and recheck the computed ratio without rounding.
  • Sample the rendered page, especially text over images, gradients, or translucent layers.
  • Review meaningful graphics and interface indicators under the non-text criterion.
  • Check that links and status messages remain distinguishable without color alone.
  • Repeat for light and dark themes and for hover, focus, selected, error, and disabled states as applicable.

6. Compare palette options without reducing the decision to one number

For two candidate palettes, compare each on the actual background and intended text size, then inspect meaningful graphics and control indicators. Check whether states and links remain understandable without color alone. Finally, consider whether the colors work coherently in the design. Contrast conformance constrains the choice; it does not establish that all passing palettes feel equally comfortable or look equally good.

7. Troubleshooting common contrast failures

Symptom Likely cause What to do
A pair reads as a pass after rounding but is below the stated threshold The computed ratio is below the strict minimum Treat it as a failure; adjust a color until the unrounded ratio meets or exceeds the threshold.
A prominent heading fails at 4.5:1 It may qualify as large text, or it may not meet the point-size and weight definition Verify rendered size and weight. Apply 3:1 only if it meets the large-text definition; otherwise use 4.5:1.
A bright, colorful pair still fails Hue difference does not ensure enough light-dark luminance contrast Change luminance by making the foreground darker or the background lighter, then recalculate.
A checker passes, but text still looks faint Thin or unusual font rendering can appear fainter even when the color ratio meets the threshold Review at the actual rendered size and weight; consider a sturdier weight or more contrast margin.
Text passes, but a control or chart is hard to interpret Text contrast does not cover every non-text visual comparison Review applicable control indicators and meaningful graphic parts under SC 1.4.11.
Success and error states become unclear without color Color is carrying meaning by itself Add a label, shape, icon, pattern, or other usable cue.
A sampled color differs from the design token The rendered page uses an overlay, opacity, image, gradient, or another composited value Check the color as it appears in context, not just the source token.

8. Keep the scope of the check clear

Color contrast checks do not cover text alternatives, keyboard behavior, focus visibility, or other accessibility criteria. A passing set of pairs is useful evidence about those pairs; it is not a complete accessibility audit. For a broader evaluation or training, Section508.gov and WebAIM describe additional resources, including professional evaluation and training options.

Frequently asked questions

Does a contrast ratio tell me which palette is easiest to read?

No. It checks a defined luminance relationship. Review the rendered design and use practical judgment about font rendering and context.

Can I use the large-text threshold for every heading?

No. The text must meet the size and weight definition: at least 18 point regular or 14 point bold.

Does checking my design tokens prove my live site passes?

No. Rendered colors can differ because of compositing or state styling. Sample and review the actual page, then check other relevant accessibility requirements separately.

Or skip the browser setup

If you need to capture a page while documenting or reviewing its rendered colors, ScreenshotNeo returns a screenshot or PDF from one API request. See the ScreenshotNeo API documentation.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
  • Cookie banners, popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
  • Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed. Response headers say whether the page was clean and whether it was billed.
  • An MCP server gives AI agents tools to take screenshots, get page information, and capture PDFs.
  • The free plan includes 1,000 screenshots a month with no card. Paid plans start at $5 for 3,000 screenshots.

Sign up free for 1,000 screenshots a month, with no card required.