I thought I had seen every way a client could break a WordPress site. Then I shipped a block theme for one who wanted a color picker on every section background, and they picked a neon lime green that turned their white text invisible. The fix was a PHP filter that calculated luminance and cached the result in a transient, purely to keep the text readable. Ugly, but it held.
If the CSS contrast-color() function had existed then, it would have saved me about six hours of debugging. You hand it a color and it hands back black or white, whichever gives better contrast against it under the WCAG contrast requirements. The browser does the arithmetic instead of you.
How the syntax works
An earlier draft of the spec called this color-contrast() and let you pass a list of candidate colors to choose from. The W3C cut that back in Color Module Level 5, so the contrast-color() function you get today takes one color and returns one color. Fewer knobs, and nothing left to argue about in code review.
/* Basic usage with a variable */
.dynamic-card {
background-color: var(--user-chosen-bg);
/* contrast-color() returns white or black automatically */
color: contrast-color(var(--user-chosen-bg));
}
/* Passing a hex value directly */
.alert-box {
background-color: #34cdf2;
color: contrast-color(#34cdf2);
}
If you have ever hit a race condition where the JavaScript color calculator loads after the first paint, you will like having this handled by the browser’s CSS engine instead. The luminance math leaves your JavaScript entirely.
Where it falls short
The function only returns black or white. If your design system calls for a specific dark grey rather than pure black, it cannot help you yet. It also ignores font-size and font-weight, both of which feed into the WCAG 2.1 thresholds.
- You get black or white, so brand-specific contrasting colors are out.
- It works on solid colors only. Text sitting over a hero image is still your problem.
- Support is experimental, so you need a fallback before this goes anywhere near production.
I have written before about implementing CSS contrast color approximation with other methods, which holds up well enough while browser support fills in.
Shipping it safely with @supports
Support for contrast-color() is still landing in Safari and Firefox, so wrap it in a @supports rule and declare a sensible default color first.
.card {
--bg: #2d5a27;
background-color: var(--bg);
/* Standard Fallback */
color: #ffffff;
}
@supports (color: contrast-color(red)) {
.card {
/* Use the function if the browser understands it */
color: contrast-color(var(--bg));
}
}
If this kind of CSS work is eating your dev hours, I can take it on. I have been building WordPress sites since the 4.x days.
What I would do with it today
The contrast-color() function moves a decision out of PHP and JavaScript and into the stylesheet, where it belongs. That matters most on block themes, where users can set any background they like and somebody still has to keep the text legible. Black and white is a narrow range for now, so add the function as an enhancement on your high-priority components and leave the fallback code exactly where it is.