UI/UX Atlas
Accessibility Intermediate

Color, Contrast & Visual Perception

Master perceptual principles and WCAG 2.2 contrast standards to design interfaces that communicate clearly for every human visual system.

9 min read

Interactive example · Contrast checker (WCAG)

The quick brown fox

jumps over the lazy dog — 14px body text sample.

4.83:1

AA
Body (4.5:1)PassLarge (3:1)PassAAA (7:1)Fail

WCAG 2.2 requires 4.5:1 for normal text and 3:1 for large text (≥24px, or ≥19px bold). Don’t rely on low-contrast “light gray on white” for body copy.

The full lesson

Color and contrast sit at the crossroads of aesthetics and function. Get them wrong and large portions of your audience — people with low vision, color blindness, aging eyes, or anyone on a washed-out laptop screen in sunlight — simply cannot use your interface. Get them right and you build clarity that benefits everyone.

This lesson goes beyond the checkbox mentality. You will learn how human vision actually processes color and luminance, what WCAG 2.2 contrast rules mean in practice, where the newer APCA model helps (and where it does not yet apply legally), and how modern color tools — especially OKLCH — make meeting those requirements far easier than manual hex-picking ever was.

How Human Vision Processes Contrast

Your eyes care far more about lightness contrast than about hue difference. The eye has three cone types (roughly red, green, and blue) plus rod cells that are essentially color-blind. Rods handle peripheral and low-light vision. The fovea — the small central area you use for reading — is packed with cones.

Here are four key perceptual facts that should shape every design decision:

  • Luminance dominates readability. Red text on a green background can be invisible to someone with deuteranopia, even if the color difference looks dramatic to someone with normal vision.
  • Simultaneous contrast makes the same gray look lighter or darker depending on what surrounds it. A neutral swatch on a dark card feels lighter than the same swatch on a white card — identical hex values, different perception.
  • Size and weight matter. Very small text or thin lines need higher contrast to stay legible than large, bold text. The traditional WCAG formula acknowledges this with a lower 3:1 threshold for large text.
  • Screen conditions vary wildly. A 4.5:1 ratio on a well-calibrated display in a dark room reads very differently on a glossy laptop under fluorescent lights.

WCAG 2.2 Contrast Requirements

WCAG 2.2 is the current legal baseline in most jurisdictions. Two success criteria govern contrast.

1.4.3 Contrast (Minimum) — Level AA

Normal text needs a 4.5:1 ratio. Large text (18pt or 14pt bold and above) needs 3:1. This is the practical floor for most products. Failing AA creates legal exposure in the US (Section 508 / ADA), the EU (EN 301 549), and other jurisdictions.

1.4.6 Contrast (Enhanced) — Level AAA

The stricter thresholds are 7:1 for normal text and 4.5:1 for large text. Most regulations do not require AAA, but it is worth targeting for body copy in document-heavy products or audiences that skew elderly.

1.4.11 Non-Text Contrast — Level AA

This one is often overlooked. UI components and graphical objects need 3:1 contrast against adjacent colors. This includes:

  • Form field borders against the background
  • Focus indicators
  • Chart lines and data point markers
  • Toggle and checkbox borders in their unchecked state

Understanding the Ratio Formula

The WCAG contrast ratio uses relative luminance (L) values:

Ratio = (L_lighter + 0.05) / (L_darker + 0.05)

Relative luminance comes from linearized sRGB values using specific gamma coefficients. You do not need to calculate this by hand. Tools like the browser DevTools accessibility panel, Figma’s contrast checker, and the Colour Contrast Analyser desktop app all implement the formula correctly.

Practical minimums to memorize:

Use caseMinimum ratio
Body text (less than 18pt / 14pt bold)4.5 : 1
Large text (18pt+ / 14pt+ bold)3 : 1
UI components and graphics3 : 1
Decorative / inactive elementsNo requirement
LogotypesNo requirement

APCA: A Better Lens, Not a Replacement Standard

The Advanced Perceptual Contrast Algorithm (APCA) is a newer model being developed as a candidate algorithm for the in-progress WCAG 3.0. It addresses real limitations in the WCAG 2.x ratio — specifically that the old formula does not account for font weight, text size, or polarity (light text on dark behaves differently from dark on light for human perception).

APCA produces a “Lightness Contrast” (Lc) score. Rough equivalents:

  • Lc 75 — body text minimum
  • Lc 60 — large bold headings
  • Lc 45 — UI components and placeholder text

How to use APCA today: treat it as a supplementary quality lens when picking type colors for your design system. Run both checks — meet WCAG 2.2 AA for legal compliance, then use APCA to catch combinations that technically pass the old formula but still feel weak on screen.

Do not present APCA as a current WCAG requirement. WCAG 3.0 is not finalized and carries no legal weight as of 2026. Teams that have swapped their compliance target to APCA are carrying legal risk.

Color Vision Deficiency: Designing for Real People

About 300 million people worldwide have some form of color vision deficiency (CVD) — meaning they perceive certain hues differently or not at all. The common types:

  • Deuteranopia / deuteranomaly — reduced sensitivity to green (most common; roughly 5% of men)
  • Protanopia / protanomaly — reduced sensitivity to red (roughly 1% of men)
  • Tritanopia — reduced sensitivity to blue (rare; affects all sexes equally)
  • Achromatopsia — complete absence of color perception (very rare)

Do

Use redundant coding: pair color with icons, labels, or patterns so status is never communicated by hue alone. A form validation state should show a red border AND an error icon AND a text label beneath the field — any one of the three is enough on its own, and together they are unmistakable regardless of how color is perceived.

Don't

Rely on red/green contrast alone to show success vs. error states. Avoid a green pill for “active” and a red pill for “inactive” with no text label or icon — users with deuteranopia see both as similar brownish-yellow tones and cannot tell them apart.

Simulation tools are your first safety net. Figma’s “Vision Simulator” plugin and browser extensions like Colorblindly let you preview your work through common CVD lenses in seconds. Build this check into your design review checklist — not as a one-off retrofit.

Dark Mode and Luminance Thinking

Dark mode is now a first-class design requirement. The outdated approach — inverting hex values and calling it done — breaks visual hierarchy and frequently fails contrast checks.

Here is the correct approach:

  • Use luminance-step elevation instead of drop shadows. On dark surfaces, shadows are invisible (dark gray on dark gray). Use lighter surface colors to show elevation instead. Material Design’s elevation-overlay system is a widely referenced example.
  • Avoid pure black backgrounds. Near-black (#0A0A0A to #121212) reduces halation — the visual bleed that makes bright text feel like it is glowing on pure black, which causes eye strain during prolonged reading.
  • Re-check every contrast ratio. Polarity reversal changes perception. A text color that just passes at 4.5:1 in light mode may pass or fail in dark mode — check both explicitly.
  • Design separate token values for each theme. The semantic token --color-text-primary should resolve to different primitive values per theme, not a programmatically inverted version of the light-mode value.

Modern Color Authoring with OKLCH

The modern approach to color authoring uses OKLCH — a perceptually uniform color space. “Perceptually uniform” means that equal numerical steps in lightness look like equal visual steps to the human eye, regardless of hue.

OKLCH defines colors with three components:

  • L — lightness (0 to 1, perceptually uniform)
  • C — chroma, or saturation (0 to roughly 0.4)
  • H — hue angle (0 to 360 degrees)

Why OKLCH beats HSL for accessibility work

In HSL, changing the hue while keeping lightness and saturation the same produces dramatically different perceived brightnesses. Yellow and blue at the same HSL lightness value look wildly different — so HSL-based tonal scales require manual luminance tweaking for every single hue.

In OKLCH, stepping L from 0.3 to 0.9 in equal increments produces colors that look evenly spaced in lightness no matter what hue you are using. This means you can build a 10-step tonal scale for any brand color algorithmically and have it pass contrast checks without trial-and-error hex editing.

CSS now natively supports OKLCH:

/* Accessible body text on white — L=0.25 gives high luminance contrast */
color: oklch(0.25 0.05 250);

/* Background token for a surface layer */
background-color: oklch(0.98 0.01 250);

And in a W3C DTCG-format design token file:

{
  "color": {
    "text": {
      "primary": {
        "$value": "oklch(0.25 0.05 250)",
        "$type": "color"
      }
    }
  }
}

The W3C Design Token Community Group (DTCG) stable JSON format uses $value and $type keys. Tools like Style Dictionary 4 consume these and output platform-specific formats (CSS custom properties, Swift, Kotlin) from a single source of truth. This eliminates the problem of maintaining separate hardcoded token files per platform.

Practical Checklist for Auditing Contrast

When auditing an existing interface or reviewing a design handoff, work through these steps in order:

  1. Automated scan first — run Axe DevTools or IBM Equal Access Checker against the rendered DOM. Automated tools catch roughly 30–40% of contrast violations without any manual effort.
  2. Check all text states — body, placeholder, helper text, and disabled labels. Disabled text is exempt from 1.4.3, but it should still read clearly as inactive.
  3. Check interactive component states — default, hover, focus, active, error, and disabled for each component type. Focus rings are the most commonly missed.
  4. Verify non-text contrast (1.4.11) — form borders, icon-only buttons, chart elements, and progress bars.
  5. Simulate color blindness — use at least deuteranopia and protanopia simulations before sign-off.
  6. Test in real environments — a conference room projector, a phone screen outdoors, and OS high-contrast mode (Windows High Contrast / macOS Increase Contrast) all reveal failures invisible on a calibrated studio monitor.
  7. Document token-level fixes — if a contrast failure traces back to a semantic token value, fix the token, not just the one instance.

Embedding Contrast in a Token-Driven Workflow

The most scalable way to maintain accessible contrast across a product is to bake it into your design token architecture:

  1. Define a primitive scale in OKLCH — ten lightness steps per hue, algorithmically generated so contrast relationships are predictable.
  2. Map semantic tokens to primitivestext-primary always maps to a step that achieves at least 7:1 on your default background; text-secondary maps to one that achieves at least 4.5:1.
  3. Encode contrast requirements as design decisions, not rules to check later — the token system makes it structurally hard to assign a low-contrast text color without deliberately overriding a semantic token.
  4. Automate contrast testing in CI — tools like axe-core and pa11y can run as part of your component library’s Storybook test suite, failing a build when a new component introduces a contrast violation.
  5. Sync tokens to Figma and code from one source — a single DTCG-format JSON file consumed by Style Dictionary for code and by Figma’s Tokens Studio plugin means the designer and engineer always work from the same source of truth.

This approach shifts accessibility from a late-stage audit to a design system property. Contrast correctness becomes structural, not aspirational.