X Xerobit

Color Accessibility — WCAG Contrast Ratios and Tools

WCAG 2.1 requires a 4.5:1 contrast ratio for normal text and 3:1 for large text. Learn how to calculate contrast ratios, test color combinations, use browser DevTools, and...

Mian Ali Khalid · · 5 min read
Use the tool
Color Picker
Pick colors, convert hex/RGB/HSL/OKLCH, and check WCAG contrast.
Open Color Picker →

Color contrast affects readability for everyone — especially for people with low vision or color blindness. WCAG 2.1 defines minimum contrast ratios that every public website should meet.

Use the Color Picker to check color values and plan accessible color pairs.

WCAG contrast requirements

LevelText typeMinimum ratio
AANormal text (< 18pt regular, < 14pt bold)4.5:1
AALarge text (≥ 18pt regular or ≥ 14pt bold)3:1
AAUI components, graphics3:1
AAANormal text7:1
AAALarge text4.5:1
  • “pt” here means CSS pixels — 18pt = 24px, 14pt = ~18.67px bold
  • Decorative images and disabled UI elements are exempt

How contrast ratio is calculated

function getLuminance(hexColor) {
  const rgb = hexToRgb(hexColor);
  const [r, g, b] = [rgb.r, rgb.g, rgb.b].map(c => {
    const srgb = c / 255;
    return srgb <= 0.04045 
      ? srgb / 12.92 
      : Math.pow((srgb + 0.055) / 1.055, 2.4);
  });
  return 0.2126 * r + 0.7152 * g + 0.0722 * b;
}

function contrastRatio(hex1, hex2) {
  const l1 = getLuminance(hex1);
  const l2 = getLuminance(hex2);
  const lighter = Math.max(l1, l2);
  const darker = Math.min(l1, l2);
  return (lighter + 0.05) / (darker + 0.05);
}

function hexToRgb(hex) {
  const clean = hex.replace('#', '');
  return {
    r: parseInt(clean.slice(0, 2), 16),
    g: parseInt(clean.slice(2, 4), 16),
    b: parseInt(clean.slice(4, 6), 16),
  };
}

// Test common combinations:
contrastRatio('#ffffff', '#000000')  // 21:1 (maximum)
contrastRatio('#ffffff', '#767676')  // 4.54:1 (just passes AA)
contrastRatio('#ffffff', '#949494')  // 2.85:1 (fails AA)
contrastRatio('#ffffff', '#6366f1')  // ~3.7:1 (fails AA for normal text)

Check common color pairs

function getWcagLevel(hex1, hex2, isLargeText = false) {
  const ratio = contrastRatio(hex1, hex2);
  
  const aa = isLargeText ? 3 : 4.5;
  const aaa = isLargeText ? 4.5 : 7;
  
  return {
    ratio: Math.round(ratio * 100) / 100,
    passAA: ratio >= aa,
    passAAA: ratio >= aaa,
    level: ratio >= aaa ? 'AAA' : ratio >= aa ? 'AA' : 'Fail',
  };
}

// Example results:
getWcagLevel('#1e293b', '#ffffff')   // { ratio: 16.1, passAA: true, level: 'AAA' }
getWcagLevel('#6366f1', '#ffffff')   // { ratio: 3.7,  passAA: false, level: 'Fail' }
getWcagLevel('#6366f1', '#1e1b4b')  // { ratio: 4.7,  passAA: true,  level: 'AA' }

Common failing combinations

White text on colored backgrounds (often fail):
- #ff0000 (red) on white: 4.0:1 (FAILS for normal text)
- #0000ff (blue) on white: 8.6:1 (passes)
- #008000 (green) on white: 4.1:1 (FAILS for normal text)
- #ff6600 (orange) on white: 2.9:1 (FAILS)
- #6366f1 (Indigo-500) on white: 3.7:1 (FAILS)

Use these instead:
- Indigo-700 (#4338ca) on white: 6.3:1 (PASSES AA)
- Red-700 (#b91c1c) on white: 5.9:1 (PASSES AA)
- Green-700 (#15803d) on white: 4.6:1 (PASSES AA)

Tailwind accessible color guide

// Tailwind palette: which shades pass AA on white/black?

// On WHITE background (need 4.5:1+):
// Slate-600 and darker: #475569+ (5.1:1) ✓
// Indigo-700 and darker: #4338ca (6.3:1) ✓
// Red-700 and darker: #b91c1c (5.9:1) ✓

// On BLACK background (need 4.5:1+):
// Slate-300 and lighter: #cbd5e1 (7.0:1) ✓
// Indigo-300 and lighter: #a5b4fc (6.7:1) ✓

CSS: WCAG-compliant text on variable backgrounds

/* Light/dark mode that maintains contrast: */
:root {
  --bg: #ffffff;
  --text: #1e293b;    /* 16:1 contrast on white */
  --accent: #4338ca;  /* Indigo-700: 6.3:1 on white */
  --muted: #64748b;   /* Slate-500: 4.6:1 on white — borderline AA */
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0f172a;
    --text: #f8fafc;    /* 18:1 contrast on dark bg */
    --accent: #818cf8;  /* Indigo-400: 5.1:1 on dark bg */
    --muted: #94a3b8;   /* Slate-400: 5.9:1 on dark bg */
  }
}

Test in browser DevTools

Chrome DevTools → Inspect element → Click color swatch → Shows contrast ratio with ✓ or ✗ for WCAG AA/AAA.

Firefox → Accessibility panel → Show contrast for any element.


Related posts

Related tool

Color Picker

Pick colors, convert hex/RGB/HSL/OKLCH, and check WCAG contrast.

Written by Mian Ali Khalid. Part of the Frontend & Design pillar.