</> HTML5Advent
ENFRESDEITPT

// css · Web Platform Advent #21

CSS contrast-color(): automatic black or white text, and its one real limit

contrast-color() picks black or white text against any background colour. The syntax, the theming case it solves, the mid-tone problem MDN warns about, and browser support.

A fan of paper paint swatches spread open, each strip printed with a colour code

contrast-color() is a CSS function that takes a colour and returns the text colour that reads best on it. It shipped across all four major engines and became Baseline Newly available in April 2026. It solves a small, annoying problem that themeable interfaces run into constantly - and it has one limit that is easy to miss.

What it actually returns

Only two values: white or black. Nothing else. The function compares the contrast of both against the colour you pass and returns whichever is higher. If the two are exactly equal, it returns white.

That narrowness is the point. It is not a general colour-derivation function and it will not produce a tinted grey or a brand-adjacent shade. It answers one question: on this background, does dark text or light text read better?

The syntax

You pass any valid <color> value, including a custom property:

button {
  background-color: var(--button-color);
  color: contrast-color(var(--button-color));
}

That single line is the case it was built for. A themeable component gets its background from a variable, and the text colour follows automatically - no second variable to keep in sync, no JavaScript reading the computed style.

Rows of colour sample boards in wooden frames standing upright
Sample boards in a showroom: every swatch needs a label that stays readable on it, which is the problem contrast-color() automates in CSS.

Where it earns its place

Light and dark mode is the clearest case. Define the background once per scheme and let the text follow:

:root {
  --background-color: navy;
}

@media (prefers-color-scheme: light) {
  :root {
    --background-color: wheat;
  }
}

body,
a {
  background-color: var(--background-color);
  color: contrast-color(var(--background-color));
}

The same pattern works for user-chosen accent colours, tag chips coloured by category, and any component library where a consumer passes a background you cannot predict. Before this function, the usual answer was a hand-maintained map of background-to-text pairs, or a build-time calculation.

The limit worth knowing

MDN publishes an explicit warning about this, and it deserves repeating: WCAG AA contrast (4.5:1) is not achievable on every background. Mid-tone colours generally fail against both black and white. The documented example is a royal blue, #2277d3: contrast-color() returns black, and black on that blue is not readable at small text sizes.

So the function tells you which of two options is better, not that the result is good enough. On mid-tone backgrounds neither option is. The practical rule is the one MDN gives: use it with colours that are clearly light or clearly dark, and keep a real contrast check in your process for anything in between.

If you are unsure whether a background falls in that mid-tone trap, run it through our WCAG contrast checker before shipping.

Browser support

Baseline Newly available since April 2026, meaning it works in the current stable versions of Chrome, Edge, Firefox and Safari, on desktop and mobile. "Newly" rather than "Widely" matters: older devices and browsers that people have not updated will not have it. Because the declaration simply fails to parse where it is unsupported, an earlier color declaration in the same rule acts as a natural fallback.

Quick reference

QuestionAnswer
Possible return valueswhite or black only
Tie between the tworeturns white
Accepts a custom propertyyes - contrast-color(var(--c))
Guarantees WCAG AAno - mid-tones fail against both
Baseline statusNewly available, April 2026
Fallbackdeclare color before it in the same rule

Use it where the background is genuinely unpredictable and clearly light or dark. Do not use it as a substitute for choosing readable colours in the first place - it makes the better of two choices, which is not the same as making a good one.