Contrastproof
Writing

Your 20px heading is not large text

2 min read

Key takeaways

  • Large text under WCAG means 24px or larger, or 18.66px or larger at font-weight 700 or more.
  • Large text needs 3:1. Everything else needs 4.5:1. There is nothing in between.
  • A 20px heading at weight 400 is normal text and needs 4.5:1, which surprises almost everyone.
  • The thresholds are defined in points (18pt and 14pt bold) and convert to 24px and 18.66px at the standard 96dpi ratio.
  • Being a heading, or an h1, or styled boldly has no bearing on the threshold. Only computed size and weight do.

The rule, exactly

WCAG gives text a lower contrast requirement when it is big enough that its strokes carry the color well. The relief is real and it is generous, but its boundary is precise and it is not where most people think it is.

TextRequirement at AARequirement at AAA
Normal4.5:17:1
Large: 24px+, any weight3:14.5:1
Large: 18.66px+ at weight 700+3:14.5:1

The specification writes these as 18pt and 14pt bold. A CSS pixel is 1/96 inch and a point is 1/72 inch, so 18pt is 24px and 14pt is 18.66px. There is no rounding allowance: 18.5px at weight 700 is normal text.

The 18 to 24px gap, where palettes die

Section labels, card titles, pull quotes and small headings cluster between 18px and 22px at regular weight. Designers reach for a lighter or more colorful tone there because it reads as a heading, and the contrast ratio quietly has to clear 4.5:1 rather than 3:1.

Here is the same color at 20px and at 24px. Nothing changed but the size, and the verdict flips:

Section label at 20px
#ff3e00 on #fbfaf920px · 400 · needs 4.5:13.39:1fix #da3400 (4.50:1)
The same color at 24px
#ff3e00 on #fbfaf924px · 400 · needs 3:13.39:1

This is not a contrived example. It is a real finding from a scan of a well-built site, where an accent orange was used for section numbers set at 20px.

Weight buys you almost six pixels

The 18.66px threshold only applies at weight 700 or more. That is a genuinely useful lever: a 19px label at weight 700 qualifies as large text, while the same label at weight 600 does not.

Be careful with variable fonts and with weights expressed as keywords. font-weight: bold computes to 700 and qualifies. font-weight: 600, which many type scales use for headings, does not.

  • 19px at 700: large text, needs 3:1.
  • 19px at 600: normal text, needs 4.5:1.
  • 24px at 300: large text, needs 3:1.
  • 18px at 900: normal text, needs 4.5:1, because 18 is below 18.66.

What to do about it

The fix is almost never to abandon the color. It is to move its lightness far enough to clear 4.5:1 while holding the hue, which usually costs less than people expect. In OKLCh you can hold hue exactly and move lightness alone, so the repaired color still reads as the same color.

The alternative is to make the type genuinely large. Pushing a 20px label to 24px is often the better design decision anyway, and it costs no color at all.

Common questions

What counts as large text in WCAG?
Text at 24px or larger at any weight, or 18.66px or larger at font-weight 700 or more. In the specification these are written as 18pt and 14pt bold.
Does a 20px heading need 4.5:1 or 3:1?
4.5:1. At weight 400, 20px is below the 24px threshold, so it is normal text. It would only qualify for 3:1 at weight 700 or more, since 20px clears the 18.66px bold threshold.
Does being an h1 or h2 change the contrast requirement?
No. The requirement depends only on the computed font size and weight. An h1 rendered at 18px at weight 400 is normal text and needs 4.5:1.
Is font-weight 600 enough to qualify for the bold threshold?
No. The threshold is weight 700 or more. A 19px heading at weight 600 is normal text and needs 4.5:1.

Find out which of these your own site does.

Free, no account, usually under 30 seconds.