Skip to content
Let’s chat ↗
THE TDP FIELD GUIDE

How to check color contrast in a UI

Color contrast compares the relative luminance of a foreground and its background. Measure the actual pair, apply the criterion that fits the content, and inspect every relevant state. A passing pair is one accessibility check; it does not certify an entire component or page.

On this page · 14 sections
WHAT YOU’LL MAKE

A contrast review table covering the text and controls in one interface.

Before you start

  • The actual foreground and background colors
  • The text size and weight
  • A list of interaction states and supported themes

Contrast is a relationship, not a property of one color

A contrast check compares a foreground with the background behind it. The same gray can be readable on one surface and insufficient on another. The task is to identify the actual pair, select the applicable requirement, and verify the component in the state people will use.

This guide focuses on an inspectable review workflow. Use the calculator as a starting point, then examine the real interface. Text over images, translucent layers, gradients, and changing backgrounds needs more than a single palette swatch.

Keep the review bounded to a screen or flow so you can record the result and fix the relevant source tokens. Passing contrast checks is one part of accessibility, not certification of the whole page.

Step 1. Identify the actual pair

Inspect the rendered text and the surface immediately behind it. Account for transparency, overlays, images, and gradients: a solid-color calculator cannot represent all of those conditions. The tool on this page accepts opaque six-digit sRGB hex colors. For more complex backgrounds, inspect the least favorable area behind the content with an appropriate tool.

Step 2. Choose the relevant criterion

WCAG 2.2 distinguishes ordinary text, large text, and certain non-text visual information. Large text means at least 18pt, or 14pt bold, approximately 24 CSS px and 18.67 CSS px respectively. Use the original criteria for exact scope and exceptions.

ContentAA contrast referenceWhat to examine
Ordinary textAt least 4.5:1Text against its background
Large textAt least 3:1Confirm the size and weight qualify
Necessary UI/state graphicsAt least 3:1Relevant adjacent colors; exceptions apply

The calculator’s UI reference is only a numeric check. It cannot decide whether a particular border is necessary to identify a control or whether an exception applies.

Step 3. Measure and record

Enter the pair into the checker. Record the original values, the unrounded result used for the decision, the content type, and the state. Do not round a value below the threshold upward to claim a pass. Keep screenshots for context, but preserve the actual values so another reviewer can reproduce the check.

Step 4. Adjust the role, not just the swatch

If a pair fails, consider changing text color, surface color, or the functional variant of an accent. Recheck the surrounding hierarchy and all uses of the token. Darkening a shared color for one button can affect links or status messages elsewhere. Avoid solving a contrast problem by increasing weight without checking whether the text still qualifies under the intended criterion.

Step 5. Review states and additional cues

Inspect default, hover, focus, selected, error, and supported themes. Read the message without relying on hue. Keyboard focus, labels, semantics, and error recovery need their own checks even when a color pair passes. Keep unresolved cases visible in the review report.

Worked example: review a settings form

Start with the form’s visible text: heading, labels, entered values, hints, errors, and button labels. Record each actual foreground and surface. Then inspect the visual information needed to recognize controls and states, such as a field boundary or selected indicator.

For ordinary text, WCAG 2.2 AA uses a minimum contrast ratio of 4.5:1; qualifying large text uses 3:1. Large text is at least 18 point, or 14 point bold. The W3C explanation of text contrast also describes exceptions and why thresholds must not be rounded up.

Do not classify a small label as large text just because it is bold. Record size and weight with the pair so another reviewer can apply the same criterion.

ElementPair to inspectAdditional question
Field labelLabel color against form surfaceIs the label associated with its control?
HintHint color against its actual surfaceIs essential information too faint?
ErrorError text against feedback surfaceIs the error also explained in words?
ButtonLabel against each supported fill stateDoes focus remain visible?
Selected controlState indicator against adjacent colorsCan the state be identified without hue alone?

Fix the role rather than one screenshot

If secondary text fails on several cards, inspect the shared semantic token and intended surfaces. A local override may fix the screenshot while leaving the same problem elsewhere. Conversely, changing a global primitive can affect roles that were already correct. Choose the level that expresses the actual problem.

Try a darker foreground, lighter surface, or a different approved pairing, then recheck the full component. Increasing saturation alone does not reliably improve contrast. Keep the visual hierarchy intact while meeting the relevant requirement.

Check transparency and changing backgrounds

An opacity value can make a color blend with what is behind it. Record both the source value and the rendered context. A label over a photograph may encounter multiple backgrounds across responsive crops, so one sampled point is not enough evidence for the whole treatment.

Consider a stable surface behind important text when the background is unpredictable. Recheck the actual implementation rather than assuming a shadow or overlay has solved every possible combination.

Include interaction states in the evidence

A default button can pass while hover or focus treatment fails. A field can become unreadable in an error state. Review supported states and themes explicitly, and record the ones you could not exercise.

Unavailable controls have specific exceptions under some contrast criteria, but an exception is not a reason to make surrounding explanations hard to read. Users still need to understand why an action is unavailable and what they can do next.

Write a useful finding

A useful report names the element, viewport or theme, foreground, background, measured ratio, applicable criterion, and suggested fix. Add a source path or screenshot so the issue can be reproduced. After the fix, record the new result and check nearby consumers of the changed token.

Use semantic color tokens to keep approved pairings consistent. For the rest of the flow, continue with the accessibility skill resource and review keyboard operation, labels, focus, and error communication as well.

Completion checklist

  • The measured pair matches the rendered interface.
  • The correct content category is used.
  • Threshold decisions use the full result, not a rounded display.
  • Changes were checked across token consumers.
  • The report separates contrast results from broader accessibility findings.

Common questions

Can this checker certify WCAG compliance?

No. It calculates a solid-color contrast ratio and compares numeric thresholds. Accessibility conformance involves more criteria and context.

Are disabled controls subject to the same contrast rules?

The contrast criteria include exceptions for inactive components. Evaluate whether the control is truly inactive and keep the surrounding explanation usable.

Sources

TRY IT HERE

Check a color pair

Use opaque six-digit hex values, such as #2e2e2e. This tool does not evaluate gradients, transparency, or full accessibility conformance.

Make the next step clear.

Check the text against the surface it actually sits on.

12.59:1AA ordinary text (4.5:1): Pass
AA large text (3:1): Pass
Non-text numeric reference (3:1): Meets threshold

Decisions use the unrounded result. Large text: at least 18pt, or 14pt bold. UI applicability and exceptions need a separate review.

color: #2e2e2e;
background-color: #fcf5f1;