Get affordable and hassle-free WordPress hosting plans with Cloudways — start your free trial today.
The light-dark()
function takes two color values—one for “light” mode and one for “dark” mode, and automatically switches between them depending on which mode is currently active.
.element {
/* black for light mode and white for dark mode */
color: light-dark(black, white);
}
It works by following the user’s color-scheme
value, which means we don’t need to reach for a user preference media query, such as prefers-color-scheme
, to toggle between modes.
It’s specified in the CSS Color Module Level 5.
Syntax
<light-dark()> = light-dark( <color> , <color> )
Arguments
/* Named color values */
color: light-dark(grey, white);
color: light-dark(lime, coral);
/* Color function values */
color: light-dark(rgb(100 100 120), rgba(255 255 255 / 0.5));
color: light-dark(hsl(120deg 100% 25% / 0.2), hsla(250deg 70% 10% / 0.5));
color: light-dark(lab(24%, 5%, 5% / 0.5), lch(10%, 2%, 20deg / 0.5));
/* Variables and custom properties */
color: light-dark(var(--light-text-color), var(--dark-text-color));
The first <color>
value is for light mode while the second <color>
value is for dark mode.
All color values and functions can be used as any of the two parameters in the light-dark()
function
Basic Usage
To begin with, we can track the user’s current color-scheme
on the :root
element, so it’s inherited down and applied to all elements:
:root {
color-scheme: light dark;
}
Then, using light-dark()
, we can set the text color to black (#000
) when light mode is active and white (#fff
) when dark mode is active:
html {
color: light-dark(#000, #fff);
}
We can do the same thing with other color properties like background-color
:
:root {
color-scheme: light dark;
}
html {
color: light-dark(#000, #fff);
background-color: light-dark(#efedea, #223a2c);
}
We may as well make variables for light and dark colors, so we can re-use them in other places as needed:
:root {
color-scheme: light dark;
--text-color-light: black;
--background-light: white;
}
html {
color: light-dark(var(--text-color-light), var(--background-light));
background-color: light-dark(#efedea, #223a2c);
}
prefers-color-scheme
vs. light-dark()
The prefers-color-scheme
method allows you to select a dark or light mode depending on whether you have it specified for both modes. It’s also a good practice to account for accessibility options (like high contrast) that would assist in the overall accessibility of your website.
By default, most browsers and operating systems select light mode. If you’re building for light and dark modes using the prefers-color-scheme
media feature, you should build for light mode first in the :root
pseudo-class then account for dark mode using prefers-color-schemes
feature.
Like this:
/* Light default mode */
:root {
--background-color: white;
--text-color: black;
--form-bg: #f0f0f0;
--form-border: #ccc;
--button-bg: #e0e0e0;
--button-text: black;
}
/* Dark Mode */
@media (prefers-color-scheme: dark) {
:root {
--background-color: black;
--text-color: white;
--form-color: white;
--form-bg: #1e1e1e;
--form-border: #555;
--button-bg: #333;
--button-text: white;
}
}
This way, your color variables and values are scoped at the highest level, making them easy to manage and override in other selectors.
In a common light and dark implementation, we could have used multiple classes/attributes and JavaScript toggles to switch between light and dark modes, changing each element’s color based on its assigned color variables.
However, we can simplify all that code using light-dark()
:
:root {
--background-color: light-dark(white, black);
--text-color: light-dark(black, white);
--form-bg: light-dark(#f0f0f0, #1e1e1e);
--form-color: light-dark(black, white);
--form-border: light-dark(#ccc, #555);
--button-bg: light-dark(#e0e0e0, #333);
--button-text: light-dark(black, white);
}
/* Light Mode */
[data-theme="light"] {
color-scheme: light;
}
/* Dark Mode - forcing this for the example, not advised to do this normally except you want to give your users the option to do so */
[data-theme="dark"] {
color-scheme: dark;
}
Notice how we are just changing values between light and dark color schemes with just one line of code. Usually, you’d place the color-scheme: light dark
in the CSS :root
pseudo-class, but since we are toggling between each mode, we can place them into two separate attributes.
This begs the questions: is light-dark()
a replacement for the prefers-color-scheme
query for light and dark mode?
Let’s inspect each again. The light-dark()
CSS function allows us to switch themes based on the CSS color-scheme
property easily. While prefers-color-schemes
allows the OS or browser to automatically pick a color mode (either light or dark). Then, light-dark()
offers more control to the user if they want to automatically switch between both color modes with CSS and JavaScript, or let the developer maintain OS preferences.
So no, it’s not a replacement.
- Do you want to leave it to the OS and not offer control to the user once they open your website? Use
prefers-color-scheme
. - Do you want to write less syntax for color mode preferences while still allowing the OS to choose between each color mode? Then use
light-dark()
.
light-dark()
Accessibility and Regarding accessibility, the biggest gripe I’ve noticed with light-dark()
is its limited colors palette. For example, we’re limited to just two states: light and dark. What about high contrast mode, or grayscale, or low contrast mode?
There is, however, the new contrast-color()
function that will help solve this issue in the future, although it’s only available on Safari Technology Preview for now. The current contrast-color()
function returns either black or white depending on which has more contrast with an input color. If your browser supports contrast-color()
, then the following demo should return the appropriate color for the text.
The CSS contrast-color()
function determines this based on an algorithm color passed into the function.
.element {
background-color: green;
color: contrast-color(green); /* Should return a white or black color */
}
Using it inside light-dark()
can help keep text color contrast for both light and dark mode such that it passes WCAG requirements for minimum contrast ratios between two color values.
.element {
background-color: light-dark(green, lightblue);
color: light-dark(contrast-color(green), contrast-color(lightblue));
}
As of this writing, high contrast mode and other properties are still on the way for the light-dark()
function. While custom values for the color-scheme
are currently written in the spec (but not yet available), meaning the value for high contrast custom mode will be able to be specified in the future.
This also means that the light-dark()
function will get updates as well, and in fact, the W3C is already planning on making that happen, not just for high contrast mode, but for any other mode that users may want specified for their website.
I will mention something though, the contrast-color()
function returns only white or black as a response to its contrasting color which is not ideal because not all colors contrast directly with black or white. In fact, it’s actually a pretty known issue that Daniel Schwarz mentions in his article on contrast-color()
and the draft mentions more control over the colors along the way.
if()
& @function
light-dark() can be replicated with CSS The light-dark()
function can be replicated with @function
. In fact, it can be used beyond color, since it can return any CSS value. This means you can use a custom light-dark function in other properties like font-family
, font-weight
, border
, and many more. Super neat if you ask me!
Here’s how it works with this simple demo from Bramus:
@function --light-dark(--light, --dark) {
result: var(--light);
@media (prefers-color-scheme: dark) {
result: var(--dark);
}
}
The --light-dark()
function above returns a value based on whether the system is set to light mode or dark mode. The only thing here is, --light-dark()
cannot respond to color-scheme
property, which, in my opinion, is not too big of a deal since we still have the light and dark mode changes visible on the screen. But, it gets better!
Note: @function
helps you create custom reusable functions that use the result
descriptor to return a specific value based on conditions set in the @function
. Juan explains it in detail in his article.
With the new CSS if()
function announced in Chrome 137, we can now make the above code a whole lot better.
@function --light-dark(--light-color, --dark-color) {
result: if(style(--scheme: dark): var(--dark-color) ; else: var(--light-color));
}
Using this in a CSS property, you would have something like:
@function --light-dark(--light-color, --dark-color) {
result: if(style(--scheme: dark): var(--dark-color) ; else: var(--light-color));
}
/* Default --scheme value to use, this is based on the system value */
:root {
--scheme: light;
@media (prefers-color-scheme: dark) {
--scheme: dark;
}
}
/* Apply color-scheme for light-dark() in built function if needed */
:root {
color-scheme: light dark;
}
/* Usage examples */
h1 {
font-size: --light-dark(2rem, 2.5rem);
font-family: --light-dark("Georgia, serif", "Helvetica Neue, sans-serif");
}
p {
font-size: --light-dark(1rem, 1.1rem);
}
The above code creates a --light-dark()
custom function using the @function
syntax and it uses the if()
inline function to dynamically switch values of any kind when the light or dark mode is set. Notice how we used the custom function to change fonts and font sizes when light or dark mode is set. This wouldn’t be possible if we just had the in-built light-dark()
function.
@function
isn’t supported by any browser yet and is only available for testing on Chrome Canary, so please hold on using this in production!
Demo
Browser support
The CSS light-dark()
function works across all major browsers. Still, it is a fully functional function that can be used in major browsers, so there shouldn’t be much worry here.
Specification
The light-dark()
function is defined in the CSS Color Level 5 specification, which is currently a Editor’s Draft and may be updated in the future.
More information
- The Future of CSS: Easy Light-Dark Mode Color Switching with
light-dark()
(Bramus) - CSS light-dark() (Mayank)
- CSS @function + CSS if() (Bramus)
- Functions in CSS?! (Juan Diego Rodríguez)