My dark mode passed WCAG. APCA disagreed.

Two contrast checks, one grey, opposite answers. Why dark mode on this site gets checked twice.

5 min read

TL;DR

  • WCAG 2 scored my dark-mode grey at 5.98 to 1, higher than the light-mode version at 4.70. On screen, the dark one looked fainter.
  • APCA agreed with my eyes: Lc 44 in dark mode against Lc 67 in light. It knows which color is the text, and it gives near-black backgrounds no free boost.
  • I lightened the dark muted and accent colors to Lc 60. WCAG 2.2 AA is still the bar every pair must pass. APCA is the second check, and in dark mode it's the one I trust.

The grey I use for dates and labels on this site is #9C948B in dark mode. I checked it against the background, #191715, and got 5.98 to 1. WCAG AA asks for 4.5. A comfortable pass.

The light mode version of the same grey scored 4.70. Barely over the line.

So by the numbers, dark mode was the more readable of the two. On screen it was the other way round. The dates in dark mode looked faint, like they were sitting behind glass.

Apr 16, 2026

Read this

Light mode muted
WCAG 4.70:1 pass
APCA Lc 67 meets 60

Apr 16, 2026

Read this

Dark mode muted, before
WCAG 5.98:1 pass
APCA Lc 44 under 60

Look at the two and decide which one you'd rather read a date in. Then look at the WCAG scores underneath.

I trusted my eyes over the number and went looking for why they disagreed.

How WCAG measures contrast

Every color has a luminance: how much light it sends to your eye, from 0 for black to 1 for white. WCAG 2 takes the luminance of the lighter color, adds 0.05, does the same for the darker one, and divides.

// WCAG 2 contrast ratio. L1 is the lighter color's luminance, L2 the darker.
const ratio = (L1 + 0.05) / (L2 + 0.05);

The 0.05 stands in for glare on the screen. On a light background it barely matters. On a near-black one, the background's luminance is close to zero, so that constant does most of the work in the bottom half of the fraction. Small changes in the text color make big jumps in the ratio. Dark pairs score higher than they look.

My background, #191715, has a luminance of 0.009. The 0.05 added to it is more than five times bigger than the color itself. Most of what the formula thinks of as "my background" is the constant.

The formula also doesn't know which color is the text. Swap them and the number stays the same. Eyes don't work that way. Light text on a dark background glows and spreads a little, more so for people with astigmatism. A thin light stroke on black is not the same as a thin black stroke on cream.

That's the gap. A ratio that ignores which way round the colors are, with a constant that flatters the dark end. Dark mode is where both land.

A second opinion

APCA, the Accessible Perceptual Contrast Algorithm, was built to match how people see contrast instead of how a light meter measures it. It gives a Lightness Contrast score, written Lc, that goes up to about 106. Higher means easier to read.

The easiest way I've found to think about Lc: WCAG measures how far apart two colors are. APCA measures how far apart they feel. Two things make it better at dark mode:

  • It knows which color is the text. Dark on light and light on dark use different curves, because the eye responds to them differently.
  • It works on a perceptual curve instead of a ratio, so a near-black background gets no free boost.

Its targets depend on what the text is for, which WCAG's single 4.5 doesn't do:

  • Lc 75 for body text, the paragraphs you read
  • Lc 60 for other text you need to read, like dates and labels
  • Lc 45 for large headings
  • Lc 30 for placeholders and text that can be skipped

Light text on dark comes out as a negative number in APCA, to show the direction. The size is what you compare, so the swatches here show it without the sign.

What it said about this site

I wrote both formulas into a small script and ran every text color on the site through them. The swatches on this page use the same code: they work out their scores from the hex values on the page, so the numbers can't drift from the colors you're looking at.

Light mode held up. Every color scored about where WCAG said it would.

Apr 16, 2026

Read this

Text
WCAG 14.93:1 pass
APCA Lc 94 meets 60

Apr 16, 2026

Read this

Accent
WCAG 8.58:1 pass
APCA Lc 80 meets 60

Dark mode didn't.

Apr 16, 2026

Read this

Muted
WCAG 5.98:1 pass
APCA Lc 44 under 60

Apr 16, 2026

Read this

Accent
WCAG 5.45:1 pass
APCA Lc 41 under 60

The muted grey is the clearest case. WCAG ranked dark mode above light mode, 5.98 against 4.70. APCA ranked them the other way, Lc 44 against Lc 67. And 44 is well under the Lc 60 that dates and labels need.

The accent did the same. Links in dark mode passed WCAG at 5.45 to 1 and scored Lc 41 in APCA, closer to placeholder text than to something you're meant to click.

My eyes had been right. The checker I trusted was the one with the blind spot.

The fix

I lightened both dark colors, keeping the hue, until each reached Lc 60.

.dark {
  --muted: #b8b2ab; /* was #9c948b: Lc 44, now 60 */
  --accent: #eba09a; /* was #e06a61: Lc 41, now 60 */
}

Apr 16, 2026

Read this

Muted, after
WCAG 8.51:1 pass
APCA Lc 60 meets 60

Apr 16, 2026

Read this

Accent, after
WCAG 8.54:1 pass
APCA Lc 60 meets 60

One more catch. Links used to fade to 80% opacity on hover, which dropped the new accent straight back to Lc 44. Now they turn the color of the text instead, so they stay readable while you point at them.

It cost something. The new accent is softer, closer to coral than red. I'd rather have a link people can read than a color I like looking at.

Nothing made me do this. Both colors already passed WCAG. Their WCAG ratios went up anyway, to 8.5 to 1, which tells you how much room the old numbers were hiding.

Why dark mode gets the extra check

This site follows your system setting. If your phone switches to dark at sunset, the dark theme isn't an option. It's the site.

Dark themes are also where designers make the most quiet compromises. Pure white on pure black is harsh, so the text comes down to an off-white and the background comes up to a warm near-black. Each move takes away contrast, and the WCAG formula is the least able to see how much.

So in light mode, WCAG is enough. In dark mode, WCAG tells me I pass, and APCA tells me whether people can read it.

The ratio was never wrong about its own math. It was answering a different question than the one I was asking.