Field Notes

Fix a failing color with the smallest move

A color fails contrast and you want to change it as little as possible. There are two short paths, lightness alone and a mix toward black or white. They often end at the same place, and when they do not, the difference is hue. Here is how far each one travels, with numbers.

Published

  • color
  • contrast
  • design tokens

A designer hands over a palette. One pair fails: the orange on white sits at 2.94:1 and needs 4.5:1. The brief is to fix it without anyone noticing. The question is which way to move, and how far.

Two short paths

Lightness only. Keep the hue and the saturation. Slide lightness toward the background's opposite until the ratio clears the line. The color stays recognisably itself.

Mix toward black or white. Blend the color with black (on a light background) or white (on a dark one) until the ratio clears. This is the straight line in RGB (red, green, blue), so it is the shortest move the numbers allow, and it drifts toward grey as it goes.

Both searches stop at the first color that passes. Both are in the Color Lab: select a color, find a failing pair in its detail panel, press "Nearest passing".

How far each travels

Distances are in RGB units, where black to white is 441.

Start Needs Lightness only Mix toward black or white
Orange #ff6600 on white 4.5:1 #c95000, moved 58 #c95000, moved 58
Grey #9ca3af on white 4.5:1 #6e7788, moved 75 #727780, moved 77
Grey #9ca3af on white, as a border 3:1 #8d95a3, moved 24 #8f95a0, moved 24
Blue #2a7de1 on navy 4.5:1 #6da6ea, moved 79 #6da6ea, moved 79
Border #d1d9e0 on white 3:1 #8197aa, moved 117 #90959a, moved 117

Two things stand out. First, the paths often meet. Darkening a color at or below half lightness is the same move as mixing it toward black, so the orange and the blue land on one answer. Second, when they differ, lightness keeps more hue and the mix keeps less, and the distance is nearly the same. The light grey border is the clearest case: the lightness path ends on a blue-grey, the mix on a neutral grey, at the same distance.

So the choice is rarely about distance. It is about whether the hue is doing a job. A brand blue should keep its hue; take the lightness path. A grey that is only a border can go either way; take whichever reads better next to its neighbours.

Why the shortest move is not always the right one

The smallest move passes one pair. A color in a palette is in many pairs. The blue that now passes on navy may have stopped passing on white. The Color Lab matrix updates when you use a fix, and the summary line says how many pairs pass before and after. Look at the whole row, not the cell you fixed.

The Contrast Circuit has a chart for the other version of this question: how much contrast you get for each unit of distance moved, under a weight that pulls the color back toward where it started. A heavy weight stops short of the line. No weight overshoots. The chart shows all five paths at once, with the one you selected drawn thicker.

Rounding

A search that stops at exactly 4.50:1 and is then written as a hex value can land at 4.48:1, because hex values are whole numbers and the search was not. The lab's fixes round first, then keep stepping until the rounded hex itself passes, and report that hex's ratio, which is the ratio the browser will compute. Every hex in the table above was checked that way. If you write your own, aim a little past the line.

Try it

Open the Color Lab, load this site's light palette, and press the accent color. The detail panel lists every pair it fails. Press "Nearest passing" on one and read both answers and both distances, then use one and watch the matrix.

Sources

  • w3.org, Understanding Success Criterion 1.4.3: Contrast (Minimum), the ratio and its thresholds
  • a11y.world, the Color Lab's nearest passing color and the Contrast Circuit's distance chart

Open the companion lab

  • One brand color cannot serve a light and a dark surface

    A link blue used on a navy header and on a pale panel fails on one of them. Moving it to fix the header breaks the panel. The only fix that passes both is two tokens, one per surface. Here are the numbers, and why no single value works.

  • Charts that still read when printed in grey

    A chart passes when it still carries its message on a grayscale printer. That means three lightness levels, not three hues; dash and shape before pattern; and labels on the data instead of a legend. This is the rule every chart on this site follows, written down.

  • Build a palette that passes every pair

    Not every pair in a palette has to pass. Every pair you use has to. Here is how to build a palette where the allowed pairs are obvious and the forbidden ones are hard to reach for.

Spotted something this note gets wrong?

Corrections and better examples are the best kind of message.

Send a correction