Journal · Color

Accessible color without the guesswork.

Perceptual color spaces and contrast checks turn a palette from a matter of taste into something you can test.

Color is usually where design discussions become a matter of opinion. One person likes the blue a little brighter; another thinks the gray is too cold. Opinions matter, but underneath them is a part of color that isn't subjective at all: whether people can read the text, see the button and tell the states apart.

That part can be measured, and when you measure it from the start, the subjective part gets much easier.

Contrast has numbers

The Web Content Accessibility Guidelines define contrast as a ratio between two colors' relative luminance, from 1:1 (identical) to 21:1 (black on white). At level AA, body text needs at least 4.5:1 against its background. Large text, roughly 24 pixels or 19 pixels bold and up, needs 3:1. Since WCAG 2.1, the boundaries of buttons, form fields and meaningful icons also need 3:1 against what's around them.

These thresholds are a floor, not a target. A light gray placeholder that technically passes can still be hard to read in sunlight on a phone. But they turn "is this readable?" into a question with an answer.

Why the usual color pickers mislead

Most tools describe color in RGB or HSL. Those models are convenient for screens, but they don't match how we see. In HSL, a yellow and a blue with the same "lightness" value look nothing alike: the yellow looks far brighter. Build a palette by stepping lightness evenly in HSL and the steps look uneven, and contrast becomes unpredictable from one hue to the next.

Perceptual color spaces fix that

OKLCH describes a color by lightness, chroma (how colorful it is) and hue, in a space designed so that equal changes look roughly equal to the eye. Two colors with the same OKLCH lightness look about equally light, whatever their hue. Modern browsers support it directly in CSS, for example oklch(0.64 0.21 286).

That property is what makes systematic palettes possible. Build every hue's scale on the same lightness steps, and step 600 of blue and step 600 of green have similar contrast against white. A rule like "text uses step 700 or darker" then holds across the whole palette.

Pick lightness for contrast, hue for meaning and chroma for mood.

A method that works

  1. Start from one brand color. Note its hue and chroma.
  2. Generate a scale of perhaps eleven steps by varying lightness evenly, easing chroma down toward the very light and very dark ends, where strong chroma can fall outside what screens can show.
  3. Choose an accent by harmony, rotating the hue, and build it on the same lightness steps.
  4. Add neutrals with a hint of the brand hue, so grays feel related rather than generic.
  5. Name colors by role, not appearance. Tokens like text-muted, surface-raised and border-subtle point at scale steps. Designers and engineers talk about the role; the value can change underneath.
  6. Check every pairing you'll actually use, text on surface and icon on button, against the thresholds above, in light and dark themes.

Don't let color carry meaning alone

Around one in twelve men and one in two hundred women have some form of color vision deficiency. A red error state and a green success state can look almost the same to them. Pair color with something else, such as an icon, a label, a pattern or a change in weight, so the meaning survives without it.

Dark mode is its own palette

Dark themes aren't the light theme inverted. Pure black backgrounds with pure white text can glare, and saturated colors look more intense on dark surfaces. We pick dark surfaces a little above black, reduce chroma for large areas, and check contrast again, because the pairings change.

Taste still matters

None of this removes the creative part. It just moves it to where it belongs. Once the system guarantees that text is readable and states are distinguishable, the discussion can focus on what the color should feel like: warm or cool, quiet or bright. That's a much better conversation to be having.

We used this method to build the color-system generator on UI/UX Atlas. Read more in the journal.