Converting Colors for Accessibility: Contrast Ratios and WCAG Compliance

May 28, 20267 min read

Color accessibility is not about avoiding certain colors — it is about ensuring enough contrast between text and background so that everyone can read your content. WCAG 2.1 defines specific contrast ratio thresholds, and converting between color formats is often necessary to calculate and fix compliance issues.

Contrast Ratio Requirements

WCAG 2.1 defines two levels of contrast compliance:

LevelNormal Text (< 18px)Large Text (≥ 18px or 14px bold)UI Components & Graphics
AA4.5:13:13:1
AAA7:14.5:1

These ratios compare the relative luminance of the foreground and background colors. A ratio of 4.5:1 means the lighter color is 4.5 times as luminant as the darker one.

Common misconception: WCAG does not ban light-gray text. It bans light-gray text on white backgrounds because the contrast falls below 4.5:1. The same gray on a dark background may pass easily.

Calculating Contrast Ratios

The contrast ratio formula requires colors in relative luminance (not hex or RGB directly):

L = 0.2126 × R + 0.7152 × G + 0.0722 × B

Where R, G, and B are linearized (sRGB → linear) channel values between 0 and 1.

function relativeLuminance(r, g, b) {
  const [R, G, B] = [r, g, b].map(c => {
    c = c / 255
    return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)
  })
  return 0.2126 * R + 0.7152 * G + 0.0722 * B
}

function contrastRatio(lum1, lum2) {
  const lighter = Math.max(lum1, lum2)
  const darker = Math.min(lum1, lum2)
  return (lighter + 0.05) / (darker + 0.05)
}

This is why converting between hex, RGB, and HSL is practical — you need numeric channel values to compute accessibility compliance.

Fixing Common Contrast Failures

When a color combination fails WCAG, you have three options:

1. Darken the text color

Lowering the lightness (in HSL) of the foreground is the most straightforward fix:

/* Fails AA: #9CA3AF on white → ratio 2.9:1 */
.text-muted { color: #9CA3AF; }

/* Passes AA: #6B7280 on white → ratio 4.6:1 */
.text-muted { color: #6B7280; }

2. Lighten the background color

Sometimes the background is the problem — dark text on a dark gray background fails just as badly:

/* Fails AA: #111827 on #374151 → ratio 2.6:1 */
/* Passes AA: #111827 on #F3F4F6 → ratio 14.7:1 */

3. Adjust both colors

For accent-colored text on accent-colored backgrounds, you often need to adjust both:

/* Fails AA: blue-500 (#3B82F6) on blue-100 (#DBEAFE) → ratio 3.1:1 */

/* Passes AA: blue-700 (#1D4ED8) on blue-100 (#DBEAFE) → ratio 5.4:1 */

Dark Mode Contrast Pitfalls

Dark mode introduces new contrast challenges. Colors that pass on white backgrounds may fail on dark ones:

CombinationLight Mode RatioDark Mode RatioPasses AA?
Gray-500 on white4.6:1Yes
Gray-500 on gray-9003.8:1No
Gray-400 on gray-9006.1:1Yes

In dark mode, muted text typically needs to be one or two shades lighter than in light mode. Use semantic color tokens with mode-specific overrides:

:root {
  --color-text-muted: #6B7280; /* gray-500, passes AA on white */
}

.dark {
  --color-text-muted: #9CA3AF; /* gray-400, passes AA on gray-900 */
}

APCA: The Next Generation

The Accessible Perceptual Contrast Algorithm (APCA) is proposed for WCAG 3.0. It addresses known weaknesses in the current 4.5:1 formula:

  • Accommodates font size and weight — larger or bolder text needs less contrast
  • Spatial frequency aware — thin lines need more contrast than large blocks
  • Better for dark mode — the current formula overestimates contrast on dark backgrounds for small text

APCA is not yet required for compliance, but tools like the APCA Contrast Checker let you preview results today. Design systems built with APCA in mind will transition more easily when WCAG 3.0 arrives.

Automated Testing

Manually checking every color combination is impractical. Automate accessibility testing:

  • axe-core — audits contrast violations in your test suite
  • Lighthouse — includes contrast checks in accessibility scoring
  • Stylelint — the color-no-invalid-hex and custom contrast rules catch issues at lint time
  • Chromatic — visual regression testing that flags contrast changes between PRs

Integrate at least one tool into your CI pipeline. Contrast failures should never reach production.

Key Takeaways

  • WCAG AA requires 4.5:1 contrast for normal text and 3:1 for large text and UI components
  • Contrast ratios depend on relative luminance — you need RGB values to calculate them
  • Fix contrast by darkening text, lightening backgrounds, or adjusting both
  • Dark mode requires separate contrast validation — light-mode-passing colors may fail
  • APCA (WCAG 3.0) will improve contrast calculation but is not yet required
  • Automate contrast testing with axe-core, Lighthouse, or Stylelint in your CI pipeline

Try It Yourself

Convert colors and check contrast compliance with our free Hex to RGB tool. Enter any hex value to get RGB, HSL, and other formats — then verify your color combinations meet WCAG requirements.