How to Fix a Failing Contrast Ratio Without Losing Your Brand Color

Contrast comes almost entirely from lightness, not hue. How to nudge a brand color to WCAG compliance in OKLCH while keeping it recognizably yours.

The audit says your brand blue fails on white. The brand team says the blue is non-negotiable. Both sides can win, because of one underappreciated fact: contrast comes almost entirely from lightness, not hue. You can keep the hue — the thing people recognize — and fix the ratio by adjusting only how light the color is.

Why lightness is the whole game

A contrast ratio compares luminance — perceived brightness. Two colors with the same luminance have a ratio near 1:1 no matter their hues: a pure red and a pure blue of equal brightness are unreadable on top of each other. So rotating hue to fix contrast is pushing on a rope. Lightness is the lever.

The OKLCH method

Modern CSS gives every color three perceptual dials: oklch(L C H) — lightness, chroma (saturation), and hue. The fix is mechanical:

  1. Convert your color to OKLCH.
  2. Hold H (hue) and C (chroma) fixed.
  3. Move L — darker for text on light backgrounds, lighter on dark — until the pair clears its threshold (4.5:1 for body text).

A worked example with a saturated azure, azure-family #1392e6 on white:

Before — 3.34:1, fails AASign in to your account
#1392E6text#FFFFFFbackground
After — 5.07:1, passes AASign in to your account
#0072BDtext#FFFFFFbackground

Same hue, same chroma; lightness lowered from L≈0.62 to L≈0.53. Still unmistakably the same blue.

If darkening isn’t an option

Sometimes even a small lightness shift is off the table — say, the exact hex is in the logo guidelines. You still have moves:

  • Flip which side changes. Keep the brand color as the background and adjust the text instead. Brand blue buttons with white text often pass while blue-on-white text fails.
  • Reserve the pure brand color for large text and graphics. Large text only needs 3:1, and decorative elements need none — use the exact hex there, and the darkened variant for body-size text.
  • Ship a two-token system. --brand (the sacred hex) and --brand-text (the accessible variant). Most design systems land here; nobody outside the design team ever notices the two blues differ.

Two traps

  • Fixing with opacity. Dropping white text to 80% opacity to “soften” it on your brand color changes the rendered color and usually drags a passing pair under the line.
  • Yellow, cyan, and lime. Some hues are intrinsically light — a yellow dark enough to pass 4.5:1 on white stops looking yellow. For those, put the color in the background with dark text instead. (Black on yellow is 19.6:1.)

Try it in the tool

PaletteSense automates the whole method: the contrast checker shows the failure, and the optimize-contrast API endpoint nudges lightness in OKLCH — hue and chroma untouched — and returns the nearest passing color. The fix suggestions on pages like gray on white are computed exactly this way.