The Accesible Perceptual Contrast Algorithm

For years, we´ve been relying on the WCAG Color contrast standard to guide us. Nowadays you can follow the Accessible Perceptual Contrast Algorithm (APCA).

Current WCAG contrast is calculated as the luminance of the foreground color divided by the luminance of the background.

(L_lighter + 0.05) / (L_darker + 0.05)

But this calculation has it's limitations because, our perception of how light or dark a color appears doesn’t match perfectly with its actual luminance value. If a color’s luminance is decreased by a certain percentage, we don’t necessarily see it as that exact percentage darker. How we perceive the change in luminance depends on several factors:

So what APCA actually computes

  1. Take text color and background color separately. This is the conceptual break. APCA needs to know which is which. There is no "the two colors", there is foreground and background.
  2. Linearize each channel with a simple power curve (~2.4 exponent), then weight to luminance using the sRGB coefficients (R 0.2126, G 0.7152, B 0.0722). Green dominates and blue contributes almost nothing to perceived lightness.
  3. Soft-clamp the black level. Instead of a flat constant, APCA models screen flare and ambient reflection with a curve that only bends the very dark values. This is what fixes the dark-end collapse.
  4. Apply different exponents depending on polarity. Dark-text-on-light gets one pair of power curves, light-text-on-dark gets another. This is where the perceptual asymmetry lives. It's the whole point, and it's why APCA's output is signed.
  5. Output an Lc value ("lightness contrast"), roughly –108 to +106. Positive = dark text on light background. Negative = light text on dark. You normally work with the absolute value, but the sign tells you which curve was used.

Tools

https://apcacontrast.com for the calculator and font table, Polypane if you want it in a browser devtools context. There's also Bridge-PCA — Somers' variant that outputs APCA-quality judgments while staying compatible with WCAG 2 thresholds, which is a reasonable bridge if you need one number to hand to a client.

Powered by Forestry.md