light-dark() isn't always the same as prefers-color-scheme
- Published at
- Updated at
- Reading time
- 3min
I've thought that the new light-dark() CSS function can be used as a drop-in replacement for prefers-color-scheme media queries. Today I learned that they don't always behave the same.
Here's how MDN describes the new color function:
The
light-dark()CSS<color>function enables setting two colors for a property [...] without needing to encase the theme colors within a prefers-color-scheme media feature query.
If you wonder about light-dark() browser support, here we go.
| 123 | 123 | 123 | 120 | 120 | 17.5 | 17.5 | 27.0 | 123 |
Now, I thought you could replace all your verbose color scheme queries with short light-dark() one-liners.
/* The old way of flipping dark mode styles */
.someElement {
background: #eee;
color: #333;
border-color: #ddd;
@media (prefers-color-scheme: dark) {
background: #333;
color: #eee;
border-color: #444;
}
}
/* The new way of flipping dark mode styles */
.someElement {
background: light-dark(#eee, #333);
color: light-dark(#333, #eee);
border-color: light-dark(#ddd, #444);
}
Unfortunately, things aren't that easy.
The first thing to realize is that light-dark() requires a properly set color-scheme. Here's MDN again:
To enable support for the
light-dark()color function, thecolor-schememust have a value oflight dark, usually set on the:rootpseudo-class.
Without setting color-scheme: light dark; on :root or a surrounding container, the light-dark() function won't work. Flip your operating system's color theme and you'll see that light-dark() doesn't work below.
Without setting color-scheme: light dark;, light-dark() isn't picking up the current color scheme...
light-dark()prefers-color-schemeThe prefers-color-scheme query, on the other hand, will react to your operating system's preferences no matter what. This different behavior seems a little strange, but I guess there are valid compatibility or legacy reasons for it. Adding color-scheme: light dark; is quickly done, right?
Everything works if color-scheme: light dark; is set!
light-dark()prefers-color-schemeWhile working on the new Web Weekly site, I implemented a color theme toggle and things got interesting.
The Web Weekly redesign is Tailwind-based and to implement a light and dark mode I added custom color variables to my tailwind to avoid placing dark:* classes everywhere.
export default {
theme: {
extend: {
colors: {
// light / dark styles
"ld-slate-50":
"light-dark(oklch(98.4% 0.003 247.858), oklch(12.9% 0.042 264.695))",
"ld-slate-100":
"light-dark(oklch(96.8% 0.007 247.896), oklch(20.8% 0.042 265.755))",
"ld-slate-100/75":
"light-dark(oklch(96.8% 0.007 247.896 / 0.75), oklch(20.8% 0.042 265.755 / 0.75))",
// ...
},
},
},
} satisfies Config;
This new color palette implements the standard Tailwind colors but uses light-dark() to automatically flip them. ld-slate-100 would be slate-100 in light mode and slate-800 in dark mode. Not gonna lie, I felt very smart when coming up with this idea.
The ld-* colors worked okay-ish until I had to fine-tune my dark mode styles and mix my automatic light-dark() color with dark:* colors using prefers-color-scheme.
My plan was to implement a dark/light mode toggle that changes the general color-scheme value and the rest should "just work", right? light dark would be the default, light should be set in light mode and dark in dark mode.
Turns out, this approach works great for light-dark() however prefers-color-scheme doesn't seem to care about color-scheme values and always relied on the OS settings.
.container {
color-scheme: light;
}light-dark()prefers-color-schemelight-dark() always reflects the currently set color-scheme. For color-scheme: light dark; it falls back to the operating system settings, it returns the light variant for color-scheme: light; and the dark one for color-scheme: dark;. It does exactly what I expected.
prefers-color-scheme isn't affected by the color-scheme value and always falls back to the operating system's color scheme. Isn't this surprising?
I don't know the exact reasoning here, but it surely must be some backwards compatibility thing. If you know, reach out and let me know. I'd love to extend this post later.
So, whenever you read somewhere that light-dark() is just a shorter prefers-color-scheme query, be aware this isn't true! If you're dealing with color-scheme-based theming, this statement is fake news...
Join 6.2k readers and learn something new every week with Web Weekly.

