How to Use CSS light-dark() for Easy Theme Switching
The CSS light-dark() function is a native styling
utility that allows developers to define two distinct color values—one
for light mode and one for dark mode—within a single declaration. By
reacting automatically to the active color-scheme value of
an element or the user's operating system preferences,
light-dark() eliminates the need for repetitive media
queries and complex class-based theming overrides. This article explains
how the function works, its core syntax, and the practical ways it
streamlines theme implementation in modern web development.
What is the CSS light-dark() Function?
Part of the CSS Color Module Level 5 specification,
light-dark() is a function that accepts two color
arguments:
color: light-dark(<light-color>, <dark-color>);The browser evaluates the element's computed
color-scheme property to determine which value to
apply:
- If the computed color scheme is
light, the first color is used. - If the computed color scheme is
dark, the second color is used.
The Prerequisite: Setting the color-scheme
The light-dark() function does not work on user system
preferences alone; it depends directly on the CSS
color-scheme property. To enable light-dark()
across your entire project, declare support for both modes on the root
element:
:root {
color-scheme: light dark;
}By defining color-scheme: light dark;, you signal to the
browser that the page supports both themes and allows user agent
preferences (such as system dark mode) to dictate which color value
light-dark() renders.
Traditional Theming vs. light-dark()
The Traditional Approach
Previously, building a dual-theme website required writing duplicated CSS variables or managing multiple media query blocks:
:root {
--bg-color: #ffffff;
--text-color: #1a1a1a;
}
@media (prefers-color-scheme: dark) {
:root {
--bg-color: #121212;
--text-color: #f5f5f5;
}
}
body {
background-color: var(--bg-color);
color: var(--text-color);
}The light-dark() Approach
With light-dark(), the process is condensed into a
single declaration without media queries:
:root {
color-scheme: light dark;
}
body {
background-color: light-dark(#ffffff, #121212);
color: light-dark(#1a1a1a, #f5f5f5);
}You can also assign light-dark() values to CSS custom
properties to maintain a reusable design system:
:root {
color-scheme: light dark;
--surface: light-dark(#ffffff, #1e1e1e);
--text: light-dark(#212529, #f8f9fa);
--border: light-dark(#dee2e6, #343a40);
}How light-dark() Simplifies Theme Switching
1. Granular and Scoped Theming
Because light-dark() responds to the local
color-scheme property, you can easily force a specific
component into dark or light mode regardless of the global theme.
.force-dark-card {
color-scheme: dark;
background-color: light-dark(#ffffff, #1e1e1e);
color: light-dark(#212529, #f8f9fa);
}Inside .force-dark-card, every nested element using
light-dark() automatically renders its dark variant without
requiring theme-specific utility classes.
2. Streamlined Manual Toggles
When implementing a manual theme toggle switch (via JavaScript), you
only need to change the color-scheme property on the root
or a wrapper element:
html[data-theme="light"] {
color-scheme: light;
}
html[data-theme="dark"] {
color-scheme: dark;
}Once the attribute updates, every CSS rule using
light-dark() instantly recalculates without needing
separate theme stylesheets or variable overrides.
3. Cleaner Codebases
By pairing colors together in a single rule, developers can see both variants side by side. This improves readability, reduces stylesheet file sizes, and prevents mismatched color variables across different states.