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.