// guide
Color and Contrast: Beyond the Ratio
Contrast ratios are the starting point, not the finish line. This guide covers who benefits from good contrast, what WCAG actually requires, how to check your work, and practical CSS techniques for building color systems that hold up across themes and user preferences.
// 01 · why contrast matters
Why Contrast Matters
When developers hear "contrast accessibility," they often think of screen reader users. But contrast primarily affects people who can see — they just can't see well enough when contrast is poor.
The numbers are significant. Approximately 300 million people worldwide have some form of color vision deficiency (commonly called "color blindness"), and around 250 million live with low vision that isn't fully correctable with glasses or contacts. These are permanent conditions, but they're only part of the picture.
Situational impairments affect everyone at some point: reading a phone screen in bright sunlight, working on a laptop in a dim room, or dealing with screen glare on a train. Age is a factor too — the lens of the human eye yellows over time, reducing contrast sensitivity. Most people over 40 experience some degree of this.
Good contrast isn't an accommodation for a small group. It's a baseline that makes content more readable for hundreds of millions of people across a wide range of circumstances.
// 02 · the contrast ratios
The Contrast Ratios
WCAG defines contrast as a ratio between the relative luminance of two colors, ranging from 1:1 (no contrast — identical colors) to 21:1 (maximum contrast — black on white). The required ratio depends on what you're styling and the conformance level you're targeting.
| Requirement | Ratio | WCAG Criterion | Level |
|---|---|---|---|
| Normal text | 4.5:1 | SC 1.4.3 | AA |
| Large text | 3:1 | SC 1.4.3 | AA |
| Enhanced (all text) | 7:1 / 4.5:1 large | SC 1.4.6 | AAA |
| Non-text elements | 3:1 | SC 1.4.11 | AA |
Note that the non-text contrast requirement (3:1) applies to UI component boundaries, focus indicators, icons that convey meaning, and parts of graphical objects needed to understand the content. Decorative graphics and logos are exempt.
// 03 · checking contrast
Checking Contrast
You don't need to calculate luminance ratios by hand. Several tools can check contrast for you at different stages of your workflow.
- Browser DevTools Chrome's color picker shows the contrast ratio directly when you inspect a text element — look for the contrast section in the color popover. Firefox's accessibility inspector also flags contrast issues and lets you simulate color vision deficiencies.
- Online calculators WebAIM Contrast Checker lets you enter two hex values and instantly see whether they pass AA or AAA for normal and large text. Useful during design review.
- Desktop applications Colour Contrast Analyser by TPGi includes an eyedropper for picking colors from any application on screen — not just the browser. Helpful for checking contrast in design tools, PDFs, or native apps.
- Automated audits axe-core and Lighthouse's accessibility audit can catch many contrast failures in CI pipelines or during development. They test rendered colors against WCAG thresholds automatically.
// 04 · non-text contrast (sc 1.4.11)
Non-Text Contrast (SC 1.4.11)
SC 1.4.11 is often overlooked because developers focus on text. But UI components and graphical objects also need at least 3:1 contrast against adjacent colors. "Adjacent" means the color immediately next to the element — usually the background, but sometimes a neighboring element.
Here's what falls under non-text contrast:
- Form field borders — the border (or other visual boundary) of text inputs, selects, and textareas must have 3:1 contrast against the surrounding background. A light gray border on a white background often fails. See the Forms pattern for accessible field styling.
- Focus indicators — the focus outline or ring must contrast at 3:1 against the background. This site uses a 2px solid outline with a color that meets this threshold in both light and dark themes.
- Icon-only buttons — if a button uses only an icon (no visible text label), the icon itself must have 3:1 contrast. The icon is the only visual affordance telling the user what the button does.
- Custom checkboxes and radio buttons — when you restyle native form controls, the custom visual must maintain 3:1 contrast for the boundary of the control and any state indicator (the check mark, the filled dot).
- Chart and graph elements — bars, lines, pie segments, and data points that need to be distinguishable must contrast against each other or against the chart background.
// 05 · color as the only indicator (sc 1.4.1)
Color as the Only Indicator (SC 1.4.1)
SC 1.4.1 (Use of Color) is one of the most frequently violated criteria. It says: color must not be the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element. You can still use color — you just can't use it alone.
text-decoration-thickness and text-underline-offset let you style it tastefully while keeping it visible.
// 06 · dark mode considerations
Dark Mode Considerations
Adding dark mode to a site effectively doubles your contrast work. Colors that pass on a light background may fail on a dark one, and vice versa. A few things to watch for:
- Light text on dark backgrounds needs re-checking. A medium blue that passes 4.5:1 against white (#fff) will likely fail against a dark gray (#1a1a2e). You need to test every text-background pair in both themes.
- Translucent overlays compound the problem. If you use
rgba()oropacityon overlays, the effective contrast depends on what's behind them. A semi-transparent dark overlay on a dark background produces very low contrast against light text. Test the computed colors, not just the overlay values. - Both themes must pass independently. It's not enough for light mode to pass and assume dark mode is fine. WCAG requirements apply to whatever the user sees.
This site uses CSS custom properties scoped to a [data-theme="dark"] selector, which makes it straightforward to define distinct values for each theme:
:root {
--color-text: #1a1a2e;
--color-bg: #ffffff;
--color-primary: #2563eb;
}
[data-theme="dark"] {
--color-text: #e2e8f0;
--color-bg: #1a1a2e;
--color-primary: #60a5fa;
}
Each value can be independently tuned to meet contrast ratios in its own context. The primary blue shifts from #2563eb in light mode to #60a5fa in dark mode — a lighter, more saturated variant that maintains sufficient contrast against the dark background.
[data-theme="dark"] scope without affecting the light theme. This is exactly the kind of issue that only shows up when you test both themes — and exactly why dark mode contrast deserves dedicated attention.
// 07 · practical css techniques
Practical CSS Techniques
CSS provides several mechanisms for building color systems that adapt to user preferences and maintain contrast across contexts.
Custom properties for theme-safe contrast
Define all colors as custom properties at the root level, then override them for dark mode. Components reference the properties, not hard-coded values, so they automatically adapt when the theme changes.
/* All color decisions happen here */
:root {
--color-text: #1a1a2e;
--color-text-secondary: #4a5568;
--color-border: #d1d5db;
--color-surface: #f7f8fa;
}
[data-theme="dark"] {
--color-text: #e2e8f0;
--color-text-secondary: #a0aec0;
--color-border: #374151;
--color-surface: #232340;
}
/* Components never use raw color values */
.card {
color: var(--color-text);
border: 1px solid var(--color-border);
background: var(--color-surface);
}
Respecting high contrast preference
The prefers-contrast: more media query lets you detect when a user has requested increased contrast through their operating system settings. Use it to bump up border visibility, text weight, or color intensity.
@media (prefers-contrast: more) {
:root {
--color-text-secondary: #1a1a2e;
--color-border: #1a1a2e;
}
[data-theme="dark"] {
--color-text-secondary: #f7f8fa;
--color-border: #f7f8fa;
}
}
Windows High Contrast (forced-colors)
Windows High Contrast Mode overrides your colors entirely. The forced-colors media query lets you detect this and adjust layout elements that depend on color — like borders that become invisible when the OS removes your custom colors.
@media (forced-colors: active) {
.button {
border: 2px solid ButtonText;
}
.card {
border: 1px solid CanvasText;
}
.focus-ring {
outline: 2px solid Highlight;
}
}
In forced-colors mode, use the CSS system color keywords (Canvas, CanvasText, ButtonText, Highlight, etc.) instead of your own color values. The browser maps these to the user's chosen high-contrast palette.
// 08 · next steps
Next Steps
Contrast is one of the few accessibility requirements an automated tool can measure precisely — but only in the states it can reach. When you verify a design, check every state (default, hover, focus, disabled) against the targets above. The Accessibility Testing Checklist folds contrast into a structured visual-and-responsive pass, and developers can confirm their implementation against the Developer Accessibility Checklist.