All entries
5 min read

Why your blues look muddy on dark backgrounds

It's not the blue. It's the luminance. Here's how to fix it without losing the brand.

Check any two colors for contrast

You picked a blue that looks great in light mode. You toggled dark mode. Now every blue on the page is swamp water.

This isn’t a bug in your toggle. Your blue hasn’t changed — your brain’s interpretation of it has. And the fix isn’t a better blue. It’s the right blue.

The luminance problem

The human eye is not equally sensitive to every wavelength. Our perception of brightness — luminance, technically — comes mostly from green (about 71%), then red (about 21%), and finally blue (about 8%). This is why a “saturated” blue like #3b82f6 carries less perceived brightness than a saturated yellow or green at equivalent RGB values.

On a white background, this doesn’t matter much. The blue has plenty of contrast against the page, the color is legible, you move on. On a black or near-black background, the same blue barely makes it out of the dark. There isn’t enough luminance difference between #3b82f6 and #0f172a for the blue to feel bright.

Run the numbers. The WCAG contrast ratio for that pair sits just under 5:1 — above AA for body text, barely. For large text it’s comfortable. For a button CTA that’s supposed to demand your eye across the page, it’s a whisper.

This is why it looks muddy. Not enough signal.

What “enough contrast” actually means

The WCAG 2.1 numbers to hold in your head:

  • 4.5:1 — minimum contrast ratio for body text (AA)
  • 7:1 — enhanced contrast for body text (AAA)
  • 3:1 — minimum for large text (18pt+) and UI components

These are floors, not targets. Hitting 4.5:1 means your text is readable, not that it’s visiblein the way you want a CTA to be visible. Aim for 7:1 or better on anything the reader’s eye needs to find first.

A medium blue like #3b82f6 on #0f172a hovers just above AA — passing, not lively. Shift 40° around the color wheel toward cyan — #22d3ee on the same background — and you cross into AAA territory (north of 10:1) without losing the cool-and-confident read.

The key insight: you don’t fix a muddy blue by making it lighter. You fix it by making it more luminous. Those are different things. (They map cleanly to the L in OKLCH, which is why that color space is better for this kind of work than HSL — see OKLCH vs HSL vs hex.)

Shift toward cyan, don’t desaturate toward gray

The intuitive fix is to lighten the blue — go from #3b82f6 to #93c5fd. This works for contrast. But it loses the punch. A light blue reads as “soft” or “timid”; your brand voice was “confident.” You’re solving one problem and creating another.

The better fix is to shift hue toward the parts of the color wheel with higher intrinsic luminance. Blue sits around 220° in HSL. Cyan sits around 180°. Teal sits around 170°. All three carry more green, which means more perceived brightness at the same saturation — and they still read as cool, adjacent, on-brand.

A dark-mode color system I’ve landed on repeatedly:

  • Keep the light-mode brand blue in the system somewhere — brand continuity, sections that still read in light mode.
  • Introduce a cyan-to-teal sibling at OKLCH L ≥ 0.7 for anything interactive on a dark background.
  • Use the teal/cyan for CTAs, links, focus rings, and any small text. Use the muted blue for larger surfaces where its moodiness is the feature, not the bug.

You end up with a two-blue palette for dark mode — one that reads as brand, one that reads as interactive — and the interactive one isn’t really blue anymore.

What this looks like in practice

Starting point:

  • Brand blue: #3b82f6
  • Dark background: #0f172a
  • Problem: ~4.8:1, muddy on CTAs

One-line fix:

  • Interactive cyan: #22d3ee
  • Background: unchanged
  • Result: 10:1+, same cool family, reads as “on” instead of “off”

The background stayed the same. The blue stayed in the system. But anything that needed to pop now has a color with the luminance to actually pop.

Every dark-mode palette has this problem

Blue is the most common case because it’s the most common brand color, but the same logic applies to purple, deep red, and any low-luminance chromatic family. If your brand color sits in the bottom third of the luminance scale, it will struggle on dark backgrounds. The fix is always: a sibling color in the same hue family but higher-luminance, reserved for interactive elements on dark surfaces.

The sooner you build that sibling into the palette, the less you’ll fight your CSS every time someone opens the app at night.

Try it
Check any foreground/background pair in under a second

Two hexes, one ratio, a live AA/AAA verdict. Paste your brand color and your dark surface — find out exactly how much luminance you're missing.

Read next

Keep going