WCAG Color Contrast: A Practical Developer's Guide
Color contrast isn't just about aesthetics — it's about whether 1 in 12 users can read your site. Here's how to meet WCAG AA and AAA in practice.
About 8% of men and 0.5% of women have some form of color vision deficiency. That's roughly 1 in 12 users — a larger audience than you might think, and one that your design choices directly affect. If your text doesn't meet minimum contrast ratios against its background, those users can't read your content. The same goes for users on bright screens in direct sunlight, users with cataracts, and users with low vision who aren't technically classified as color-blind.
The Web Content Accessibility Guidelines (WCAG) define specific contrast ratios that pass accessibility benchmarks. Hitting them isn't optional if you care about reaching your full audience — and in many jurisdictions, it's legally required.
The four contrast levels
WCAG 2.1 defines contrast requirements for two conformance levels:
| Level | Normal text (< 18pt or < 14pt bold) | Large text (≥ 18pt or ≥ 14pt bold) | |-------|-------------------------------------|-------------------------------------| | AA (minimum) | 4.5:1 | 3.0:1 | | AAA (enhanced) | 7.0:1 | 4.5:1 |
AA is the legal minimum in most jurisdictions (EU, US for federal sites, UK under the Equality Act). AAA is the gold standard for content where readability matters most: long-form articles, legal documents, healthcare information.
The contrast ratio is calculated as:
(L1 + 0.05) / (L2 + 0.05)
Where L1 is the relative luminance of the lighter color and L2 is the relative luminance of the darker color. The + 0.05 accounts for ambient light flare on the screen.
Calculating relative luminance
To compute the contrast ratio, you first need the relative luminance of each color. For sRGB colors (the standard web color space):
- Convert each RGB channel from 0-255 to 0-1.
- For each channel, if the value is ≤ 0.03928, divide by 12.92. Otherwise, apply the gamma correction:
((value + 0.055) / 1.055) ^ 2.4. - Combine the channels:
L = 0.2126 * R + 0.7152 * G + 0.0722 * B.
The coefficients (0.2126, 0.7152, 0.0722) reflect human visual sensitivity — we're much more sensitive to green light than to blue.
This is why a pure blue (#0000FF) on white (#FFFFFF) has poor contrast (4.6:1 — barely AA for normal text), while pure green (#00FF00) on white has good contrast (1.4:1 — fails, actually, because green-on-white is too bright).
Common contrast failures
1. Gray text on white. A common design trend uses #999999 or #AAAAAA for secondary text. #999999 on white has a contrast ratio of 2.85:1 — well below AA. Bump to #666666 (5.74:1) or #767676 (4.54:1) to pass.
2. Brand colors as text. A brand color that looks great in a logo may not work as text. A bright orange (#FF6600) on white has a contrast ratio of 3.04:1 — below AA for normal text. Either darken the brand color for text use, or use it only for large text and decorative elements.
3. White text on accent colors. A bright accent color (#3B82F6, the Tailwind blue-500) on white text has 4.0:1 contrast — fails AA for normal text. Darken the accent (#1D4ED8, blue-700) for 6.5:1 contrast, or use white text on the accent (#3B82F6 on white text → 4.0:1 → fail; white on #1D4ED8 → 6.5:1 → pass).
4. Hover state changes. A button that's accessible in its default state may become inaccessible on hover. Test both states.
5. Placeholder text in inputs. Browser defaults often fail AA. Explicitly set placeholder colors with sufficient contrast.
Designing for accessibility from the start
The cheapest way to meet contrast requirements is to design for them from the start. Retrofitting contrast into an existing design is painful because:
- Brand colors are baked into the design system
- Hover and active states need re-balancing
- Light and dark themes both need re-verification
- Every component needs re-testing
When starting a new design:
- Define a small color palette with explicit foreground/background pairs that meet AA.
- Test with a contrast checker before adding colors to the design system.
- Verify dark mode separately — different colors fail in different directions.
- Audit the final design with automated tools (axe, WAVE, Lighthouse) and manual review.
Tools for measuring contrast
Browser DevTools have built-in contrast checkers. In Chrome, inspect any text element and look for the contrast ratio next to the color picker. It shows pass/fail for AA and AAA.
For design files, contrast checkers exist as Figma plugins, Sketch extensions, and standalone web apps. Use them at every stage of the design process.
When you need to compute contrast programmatically (e.g., for a custom theme builder), the formula is simple to implement. TextOpal's Color Converter includes a contrast checker that takes any foreground and background color (HEX, RGB, or HSL) and shows the contrast ratio with pass/fail badges for AA Normal, AA Large, AAA Normal, and AAA Large. A one-click swap button flips the colors to test both directions.
The dark mode trap
Dark mode introduces a new set of contrast challenges. A design that passes AA in light mode may fail in dark mode, and vice versa.
Common dark mode mistakes:
- Pure white text on pure black. Contrast is 21:1 (maximum), but the stark contrast causes eye strain for many users. Use off-white (#F0F0F0) on near-black (#1A1A1A) for a 16:1 ratio that's gentler.
- Saturated colors on dark backgrounds. A bright blue (#3B82F6) on dark background (#1A1A1A) has 5.4:1 contrast — passes AA. The same color on slightly lighter dark (#2A2A2A) drops to 4.6:1 — barely passes.
- Dimming secondary text too much. Dark mode design often pushes secondary text to very low contrast, assuming dark backgrounds are easier on the eyes. This isn't true for everyone — users with astigmatism often find dark mode harder to read, especially with low-contrast text.
Test dark mode separately. Don't assume that a passing light-mode design will pass in dark mode.
Beyond contrast: full WCAG considerations
Contrast is just one of WCAG's accessibility requirements. Other considerations:
- Color shouldn't be the only signal. Don't rely on color alone to convey meaning (e.g., red text for errors). Add icons, text labels, or underlines.
- Focus indicators must be visible. The default browser outline works; removing it without replacement is an accessibility failure.
- Text resizing must work up to 200% without breaking layout.
- Keyboard navigation must reach every interactive element.
These are separate concerns from contrast, but they're often grouped together in accessibility audits.
Legal and ethical obligations
In the EU, the Web Accessibility Directive requires public sector sites to meet WCAG 2.1 AA. In the US, the Americans with Disabilities Act (ADA) has been interpreted to apply to websites — recent lawsuits have forced major retailers to settle accessibility claims.
Even where not legally required, accessibility is an ethical imperative. Excluding 8% of your potential audience because of design choices that could be fixed with a contrast check is shortsighted.
The minimum viable check
If you do nothing else, do this:
- Open your site in Chrome DevTools.
- Inspect every text element.
- Look at the contrast ratio next to the color picker.
- Fix anything that fails AA.
This takes 10 minutes for a small site and catches the most common accessibility failures. It's not a complete audit, but it's a floor — and it's dramatically better than doing nothing.
For ongoing work, build contrast checks into your design system, your CI pipeline, and your component library. Accessibility isn't a one-time audit; it's a continuous practice. The contrast ratio you ship today is the readability your users experience tomorrow.