ADA Compliance Professionals

    Text color contrast must meet WCAG AA

    Last updated:

    Who it helps:
    Blind
    Standard:
    WCAG 2.2 Level AA

    Text color contrast must not fall below WCAG AA thresholds

    All text must maintain sufficient contrast against its background. This includes headings, body copy, links, buttons, form labels, captions, and text within images. Users with low vision or color-vision deficiencies, and anyone in bright glare or on low-quality displays, are affected.

    Why It Matters

    Low contrast makes words blend into the background. People may misread prices, miss error messages, or fail to see links and buttons. Cognitive load and eye strain increase, especially for dense content.

    Contrast issues are amplified on mobile, outdoors, and for older users or those with color-vision deficiencies. Clear contrast improves readability for everyone and reduces mistakes in critical tasks like forms and checkout.

    Common Causes

    • Brand colors chosen without checking luminance contrast
    • Text placed over images, video, or gradients without a solid or darkened backdrop
    • Small font sizes, thin/light weights, or low-opacity text
    • Hover, focus, visited, and disabled states not tested for contrast
    • Transparent overlays that lower contrast unintentionally
    • Dark mode or theme switches that invert colors without revalidating contrast

    How to Fix

    1. Identify all text: headings, paragraphs, links, buttons, form fields, placeholders/assistive text, captions, badges, and text in images.
    2. Apply WCAG 2.2 thresholds (SC 1.4.3 Contrast Minimum):
      • Normal text: at least 4.5:1 against the background
      • Large text: at least 3:1 (large = 24px regular or 19px bold or larger)
      • Notes: Logotypes and purely incidental/inactive text are exempt, but aim to meet ratios whenever practical.
    3. Choose compliant color pairs:
      • Use a contrast checker to validate each foreground/background pair for each size/weight.
      • Verify all states (default, hover, focus, active, visited, disabled) maintain the required ratio.
    4. Avoid text in images:
      • Prefer real text over images. If text must appear over imagery, add a solid background or a sufficiently opaque overlay to achieve the ratio across the whole text area.
    5. Strengthen link visibility:
      • Do not rely on color alone to indicate links. Keep an underline or add another clear visual cue, and ensure the link color meets contrast requirements against the page background.
    6. Codify tokens and themes:
      • Define accessible color tokens and reuse them. Validate both light and dark themes and any high-contrast modes.
    7. Re-check after layout changes:
      • Gradients, transparency, and layered elements can change effective contrast. Re-test when backgrounds or overlays shift.

    Recommendation: Where feasible, exceed AA (e.g., aim for ≥7:1 for body text) to improve readability in varied conditions.

    How to Test

    • Automated checks:
    • Use a contrast checker or accessibility testing tools (e.g., browser devtools contrast, axe, WAVE) to flag insufficient pairs.
    • Confirm ratios match the actual font size/weight rendered in CSS.
    • Manual visual checks:
    • Inspect text over images/gradients at multiple positions; ensure the ratio is met across all parts of the text.
    • Zoom to 200% and confirm text remains readable and meets the same ratios.
    • View on a mobile device in bright light; verify readability.
    • Keyboard check:
    • Tab through interactive elements. Ensure the text color and the focus/hover/active states still meet the required contrast against their backgrounds.
    • Ensure the focus indicator itself is clearly visible (non-text contrast should be ≥3:1).
    • Screen reader check:
    • Contrast is not announced by screen readers. If text is embedded in images, ensure equivalent alt text is provided and visible text still meets contrast.
    • Mobile/touch check:
    • Test light/dark mode, system high-contrast settings, and varying brightness. Confirm ratios remain valid and no state falls below thresholds.

    Good Example

    HTML
    <html>
      <head>
        <style>
          :root {
            --bg: #ffffff;
            --text: #1b1b1b;          /* ~15:1 on white */
            --brand: #084c9e;         /* ~8:1 on white for normal text */
            --on-brand: #ffffff;      /* ~10:1 on #084c9e */
            --overlay: rgba(0,0,0,0.5);/* ensures legible text on images */
          }
          body { background: var(--bg); color: var(--text); font: 16px1.6 system-ui, sans-serif; }
          a { color: var(--brand); text-decoration: underline; }
          a:hover, a:focus { text-decoration: underline; }
          .btn { background: var(--brand); color: var(--on-brand); padding: 0.75rem 1rem; border: none; }
          .hero {
            position: relative; color: #ffffff;

    background: url('mountains.jpg') center/cover no-repeat;

    padding: 3rem 1rem;

    }

    .hero::before {

    content: ""; position: absolute; inset: 0; background: var(--overlay);

    }

    .hero > * { position: relative; }

    .hero h1 { font-size: 2rem; font-weight: 700; } /* large text, 3:1 minimum */

    .hero p { max-width: 40rem; }

    </style>

    </head>

    <body>

    <main>

    <h1>Account settings</h1>

    <p>Update your profile and security preferences.</p>

    <a href="#">View security tips</a>

    <button class="btn">Save changes</button>

    <section class="hero">

    <h1>Plan your next trip</h1>

    <p>Find routes, weather, and permits with confidence.</p>

    </section>

    </main>

    </body>

    </html>

    Bad Example

    HTML
    <html>
      <head>
        <style>
          body { background: #ffffff; color: #9aa3ad; font: 14px1.4 Arial, sans-serif; } /* light gray on white, too low */
          a { color: #7fb6ff; text-decoration: none; } /* link not underlined and low contrast */
          .banner {

    background: url('city.jpg') center/cover no-repeat; color: #ffffff;

    padding: 2rem 1rem; /* no overlay; text blends with bright areas */

    }

    .muted { opacity: 0.6; } /* reduces contrast further */

    </style>

    </head>

    <body>

    <div class="banner">

    <h2>Summer sale</h2>

    <p class="muted">Limited time only</p>

    </div>

    <p>Shipping updates available here: <a href="#">status page</a></p>

    </body>

    </html>

    Quick Checklist

    • Normal text meets ≥4.5:1 contrast; large text (≥24px regular or ≥19px bold) meets ≥3:1
    • All states (default, hover, focus, active, visited, disabled) meet the same ratios
    • Links are not color-only: provide an underline or another clear cue
    • Text over images/gradients has a solid background or strong overlay
    • No low-opacity text that drops effective contrast
    • Light and dark themes validated; high-contrast/system settings do not break ratios
    • Prefer real text over images; if unavoidable, ensure alt text and required contrast
    • Re-test after any color, layout, or overlay change