Contrast and colour

Meet the ratios, and stop using colour as the only way to say something.

13of 16
≈ 12Min read
4Parts

The numbers

The ratio is computed from the relative luminance of the two colours: identical colours are 1:1, black on white is 21:1. On a white background, #767676 is exactly 4.54:1 — the lightest grey that passes for body text, and a number worth memorising.

WhatAAAAA
Body text4.5:17:1
Large text — 24px, or 18.66px bold3:14.5:1
The visual boundary of a control, and meaningful graphics3:1—
Disabled controls, logos, purely decorative thingsno requirement—
Placeholder text is text

So is a disabled-looking label that is not actually disabled, so is a "subtle" caption, and so is white text on a brand colour. The two that fail in almost every design system are placeholder grey and a mid-tone brand colour used for links.

Contrast is not only text

The boundary that tells you where a control is, the focus ring, the bars in a chart, an icon that carries meaning: all 3:1. A form whose fields are outlined in #eee is a form that some people cannot find.

WCAG 2.1 added non-text contrast, and it catches a lot
input { border: 1px solid #ddd; }     /* 1.3:1 against white — the field is invisible */
input { border: 1px solid #767676; }  /* 4.5:1 — comfortably past the 3:1 needed */

:focus-visible { outline: 2px solid #00eadc; }   /* must reach 3:1 against what is behind it */

Never colour alone

This is a separate criterion from contrast and it is the one that catches required fields marked only in red, links inside text that are only a different colour, chart series distinguished only by hue, and error states shown only by a red border.

Underlining links inside body text is the classic fix. If the design refuses, they need at least 3:1 against the surrounding text and a non-colour indicator on hover and focus.

the same information, twice
<!-- invisible to 8% of men, and to anyone on a bad screen -->
<span style="color: red">Out of stock</span>

<!-- colour, plus a word, plus a shape -->
<span class="stock stock--out">✕ Out of stock</span>

<!-- and in a chart: different dash patterns or markers, not only different colours -->

Designing with it rather than auditing for it

  • Pick text colours from a small scale where every combination is already checked, and put the ratio in the token name if it helps: --text-muted is a decision, not a hex code somebody picked.
  • Check the dark theme separately — a palette that passes on white often fails on graphite, and vice versa.
  • Check the hover and disabled states, the focus ring, and text over images (an overlay or a gradient scrim is the usual answer).
  • Test with the browser's contrast tool in the inspector; it shows the ratio live as you change the colour.

Practice