CSS contrast-color() for Readable Text

Readable text without manual contrast checks

Picking readable text used to mean hardcoding color: white or color: black for each background. contrast-color() does the math automatically, returning whichever meets WCAG AA contrast against the given color.

Old way 10 lines
/* Hardcode a text color per background */.badge-blue   {  background: #1e40af;  color: white;}.badge-yellow   {  background: #fde047;  color: black;}/* Repeat for every color variant */
Modern
4 lines
.badge   {  background: var(--bg);  color: contrast-color(var(--bg));}
Limited availability 6% global usage

This feature is not Baseline because it does not work in some of the most widely-used browsers.

Not ready for production without a fallback.

146+
26+
contrast-color() picks white or black automatically
Badge
Badge
Badge
Badge
Badge
Badge
Badge
Badge
color: contrast-color(var(--cc-bg))

Zero guesswork

The browser calculates contrast automatically. No WCAG math, no hardcoded overrides for each background.

Themeable by default

Change --bg to any color and the text color adjusts. Works with dynamic colors and user preferences.

Accessible by design

contrast-color() targets WCAG AA contrast. Readable text is the default, not an afterthought.

Old Approach
Manual
Hardcoded per background
Modern Approach
contrast-color()
Automatic, accessible
Lines Saved
10 → 4
Per color variant

How it works

contrast-color(bg) takes a background color and returns either black or white, whichever meets WCAG AA contrast requirements against that background. No JavaScript, no preprocessor, no lookup table.

The old approach meant manually choosing color: white or color: black for each background, and repeating that decision every time a new color was added. With user-generated colors or dynamic themes, this quickly becomes unmanageable in plain CSS.

Combine it with custom properties for maximum flexibility: color: contrast-color(var(--bg)) means you only define --bg and the browser handles the rest. Support is still early-stage but it is an Interop 2026 focus, so all major browsers are actively implementing it.

ESC