DEV Community

Snappy Tools
Snappy Tools

Posted on • Originally published at snappytools.app

WCAG Color Contrast Explained: Why Your Text Might Be Failing Accessibility

If you've ever run an accessibility audit and seen "contrast ratio fails WCAG AA" — this post explains what that actually means, how the math works, and when each threshold applies.


What is color contrast ratio?

Contrast ratio is a number between 1:1 and 21:1 that describes how different two colors appear to a human eye.

  • 1:1 = identical colors (white on white, invisible)
  • 21:1 = maximum contrast (black on white)

The ratio is calculated from the relative luminance of both colors — a measure of how bright a color appears to human perception, weighted for how our eyes respond to different wavelengths (we're more sensitive to green than blue, for example).


The WCAG standards

The Web Content Accessibility Guidelines (WCAG) define two levels:

AA (the baseline — required for most sites)

Element Minimum ratio
Normal text (< 18pt or < 14pt bold) 4.5:1
Large text (≥ 18pt or ≥ 14pt bold) 3:1
UI components and graphics 3:1

AAA (enhanced — harder to achieve)

Element Minimum ratio
Normal text 7:1
Large text 4.5:1

Which level do you need? WCAG AA is the standard referenced in most legal accessibility requirements (ADA in the US, EN 301 549 in the EU). AAA is aspirational — some WCAG documents note it can't always be achieved for all content.


How the math works

The formula for contrast ratio is:

(L1 + 0.05) / (L2 + 0.05)
Enter fullscreen mode Exit fullscreen mode

Where L1 is the lighter color's relative luminance and L2 is the darker one's.

Relative luminance is derived from the RGB values after converting from sRGB (the gamma-corrected color space your screen uses) to linear light values:

// Simplified
function relativeLuminance(r, g, b) {
  const [rLin, gLin, bLin] = [r, g, b].map(c => {
    const s = c / 255;
    return s <= 0.04045 ? s / 12.92 : Math.pow((s + 0.055) / 1.055, 2.4);
  });
  return 0.2126 * rLin + 0.7152 * gLin + 0.0722 * bLin;
}
Enter fullscreen mode Exit fullscreen mode

The weighting (0.2126, 0.7152, 0.0722) reflects that human eyes are most sensitive to green and least sensitive to blue.

You don't need to do this by hand — but understanding why light gray on white fails while dark gray passes makes the rule make more sense.


Common mistakes and how to fix them

1. Light gray text on white backgrounds

/* Fails AA — ratio ~4.0:1 */
color: #767676;
background: #ffffff;

/* Passes AA — ratio 4.5:1 */
color: #737373;
background: #ffffff;
Enter fullscreen mode Exit fullscreen mode

One shade darker. That's often all it takes.

2. Colored text on colored backgrounds

Brand colors frequently fail. A blue button with white text might look readable but still fail.

/* Many brand blues fail with white text */
background: #4A90E2; /* ratio ~2.9:1 with white — fail */
background: #1a73e8; /* ~4.6:1 with white — pass */
Enter fullscreen mode Exit fullscreen mode

3. Placeholder text

Placeholder text in inputs is usually styled with low opacity or light gray. It's exempt from contrast requirements in WCAG 2.1 — but it's still bad UX to make it unreadable.

4. Disabled states

Disabled form elements are also exempt from contrast requirements under WCAG.


Which pairs pass?

Some quick reference points for text on white (#ffffff):

Text color Ratio Result
#000000 (black) 21:1 ✅ AAA
#595959 7.0:1 ✅ AAA
#767676 4.5:1 ✅ AA
#949494 2.9:1
#cccccc 1.6:1

How to check contrast quickly

There are a few ways to check:

In browser DevTools: Chrome and Firefox both show contrast ratio in the color picker when you click a color in the Styles panel. Look for the WCAG badge.

In design tools: Figma's accessibility plugin and Sketch's Stark plugin show contrast as you pick colors.

Standalone checker: I built a free, client-side tool that checks any two colors instantly: SnappyTools Color Contrast Checker

It shows the ratio, WCAG AA/AAA pass/fail for normal text, large text, and UI components — and lets you swap foreground and background to test both directions.


The practical approach

Don't start from scratch trying to hit 4.5:1. Instead:

  1. Pick your background color
  2. Use a checker to find the darkest text shade that fits your palette
  3. For link colors (often the hardest): check both against the background AND against surrounding body text (links inside paragraphs need 3:1 against adjacent text under WCAG 1.4.1)

Most accessibility failures come from light gray body copy, faint labels, and light-colored buttons with white text. Fix those three and you'll clear most audits.


One more thing: don't rely on color alone

WCAG 1.4.1 says information can't be conveyed only by color. Red error text passes contrast but still fails if a color-blind user can't distinguish it from normal text without an icon or label.

Pair color with icons, labels, or patterns where the meaning matters.

Top comments (0)