Color Contrast Explained
Color contrast is how different two colors are from each other in terms of brightness, not hue. Two colors can look visually distinct (say, red and green) and yet have poor contrast for someone with color blindness, low vision, or anyone reading in bright sunlight.
WCAG defines contrast as a ratio between the relative luminance of two colors. SC 1.4.3 (Contrast Minimum, Level AA) sets the thresholds. SC 1.4.11 (Non-text Contrast, Level AA) extends this to UI components.
What is Relative Luminance?
Relative luminance is a measure of perceived brightness, on a scale from 0 (absolute black) to 1 (absolute white). It's not the same as lightness in HSL, it's a weighted calculation that accounts for how the human eye perceives red, green, and blue differently.
Green appears brighter to most people than blue at the same intensity. The luminance formula weights green at about 71%, red at 21%, and blue at only 7%. This is why two colors that look "different" can still be hard to distinguish for someone with reduced contrast sensitivity.
The contrast ratio between two colors is calculated as:
(L1 + 0.05) / (L2 + 0.05)
Where L1 is the relative luminance of the lighter color and
L2 is the luminance of the darker color. The ratio ranges from
1:1 (identical colors, no contrast) to 21:1 (black on white, maximum contrast).
The WCAG Thresholds
| Text type | Minimum ratio | Level |
|---|---|---|
| Normal text (under 18pt regular / 14pt bold) | 4.5:1 | AA |
| Large text (18pt / 24px regular or larger, OR 14pt / 18.67px bold or larger) | 3:1 | AA |
| Normal text (enhanced) | 7:1 | AAA |
| Large text (enhanced) | 4.5:1 | AAA |
| Incidental text (inactive UI, purely decorative) | No requirement | N/A |
| Logotypes (text in logos or brand imagery) | No requirement | N/A |
The 18pt threshold matters: 18pt equals exactly 24px at standard screen resolution. If your heading is 24px or larger and set in normal (not bold) weight, it qualifies as "large text" and only needs 3:1.
Non-text Contrast (SC 1.4.11)
SC 1.4.11 (Level AA) extends contrast requirements beyond text. UI components and graphical elements must also meet a minimum 3:1 contrast ratio against adjacent colors. This covers:
- The borders of form inputs (text fields, checkboxes, radio buttons, selects)
- Focus indicators, the outline that appears when an element is keyboard focused
- Icons that convey information (not purely decorative icons)
- Chart lines, data points, and boundaries between data series
The most common failure here is a light gray border on a white background: a classic look in modern design that often fails the 3:1 threshold.
What counts as "adjacent"? The contrast is measured against the color immediately next to the component's visual boundary, usually the page background or a containing element's background color.
Testing Your Colors
You don't need to calculate luminance by hand. Several free tools do it instantly:
- WebAIM Contrast Checker, paste two hex codes and get the ratio immediately, with pass/fail indicators for each WCAG level.
- Browser DevTools, Chrome and Firefox both show contrast ratios in the color picker within the accessibility inspector.
- Colour Contrast Analyser (by TPGi, free desktop app) , lets you eyedropper colors directly from any application on your screen.
- Figma and Sketch plugins, several plugins check contrast ratios directly inside your design tool, before any code is written.
Test with your actual rendered colors. Sometimes background gradients, images, or opacity affect the effective contrast in ways that aren't obvious from design files alone.
Common Failures
- Light gray placeholder text
- Placeholder text in form fields is typically displayed in a light gray, often around #999 or lighter, on a white background. This frequently fails 4.5:1. Replace light placeholders with visible labels (which have the added benefit of not disappearing when the user starts typing).
- Disabled state text
- Disabled controls are exempt from contrast requirements ("incidental text"). However, consider whether your users can tell why a control is disabled and what they need to do to enable it. Purely low-contrast disabled states can create confusion even when they're technically compliant.
- White or light text on brand colors
- Many brand palettes include vivid mid-range colors, medium blues, greens, oranges, that fail 4.5:1 with white text. A color that looks bold and vibrant on a presentation slide may not provide enough contrast for body text on a screen.
- Text over images or gradients
- When text sits on a background image or gradient, contrast must be evaluated at every point along the text, not just the "average" background color. If the background is varied, add a semi-opaque text container or text shadow.
Color is Not Enough on Its Own
SC 1.4.1 (Use of Color, Level A) is a related but separate requirement: color cannot be the only means of conveying information.
Even if your red error message meets 4.5:1 contrast against its background, you cannot rely on red alone to communicate "this is an error." Some users do not perceive color differences at all. Add an icon, a text label, a border, or any other non-color visual cue alongside the color.
The same applies to links that are only distinguished from body text by color. Underlines are not just decoration, they provide a second visual signal that doesn't depend on color perception.