Featured image of post Implementing Simple Dark Mode with CSS light-dark() Featured image of post Implementing Simple Dark Mode with CSS light-dark()

Implementing Simple Dark Mode with CSS light-dark()

Optimize variables with modern light-dark() CSS color utility, letting browsers auto-toggle variables seamlessly.

The Problem with Traditional Dark Mode

Implementing dark mode traditionally required duplicate CSS rules wrapped in @media (prefers-color-scheme: dark):

:root {
  --bg: #ffffff;
  --text: #111111;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #1a1a2e;
    --text: #e0e0e0;
  }
}

This works but creates maintenance overhead, especially when managing multiple color properties across large stylesheets. The light-dark() CSS function eliminates this duplication entirely.

The light-dark() Function

The light-dark() function accepts two color values and automatically returns the one matching the user’s current color scheme:

color: light-dark(black, white);
/* Returns black in light mode, white in dark mode */

The first argument is the light-mode color, the second is the dark-mode color. The function respects the user’s system preference or the color-scheme property.

Setting Up color-scheme

For light-dark() to work, you must declare which color schemes the page supports:

<meta name="color-scheme" content="light dark" />

Or in CSS:

:root {
  color-scheme: light dark;
}

This tells the browser the page supports both schemes and enables the light-dark() function.

Simplifying Theme Variables

With light-dark(), the previous example becomes a single declaration:

:root {
  color-scheme: light dark;
  --bg: light-dark(#ffffff, #1a1a2e);
  --text: light-dark(#111111, #e0e0e0);
  --primary: light-dark(#2563eb, #60a5fa);
  --border: light-dark(#d1d5db, #374151);
  --shadow: light-dark(rgba(0,0,0,0.1), rgba(0,0,0,0.4));
}

No media queries needed. The browser handles the switching automatically.

Comparison: Media Query vs light-dark()

Aspect@media prefers-color-schemelight-dark()
Lines of code~10 per variable pair1 per variable
MaintenanceDual blocks to updateSingle line
JavaScript toggleNeeds manual class togglingCompatible with color-scheme override
Browser supportUniversalChrome 123+, Firefox 128+, Safari 17.5+
GranularityPer-blockPer-value

Toggling Programmatically

For manual theme toggles (not just system preference), change the color-scheme on the root element:

// Toggle between light and dark manually
document.documentElement.style.colorScheme =
  document.documentElement.style.colorScheme === "dark"
    ? "light"
    : "dark";

This works seamlessly with light-dark() because the function reads the current color-scheme value.

Combining with Custom Properties for Complex Themes

For full control, combine light-dark() with semantic custom properties:

:root {
  color-scheme: light dark;
  /* Surface colors */
  --surface-primary: light-dark(#ffffff, #0f172a);
  --surface-secondary: light-dark(#f8fafc, #1e293b);
  /* Text colors */
  --text-primary: light-dark(#0f172a, #f1f5f9);
  --text-secondary: light-dark(#475569, #94a3b8);
  /* Accent colors */
  --accent: light-dark(#3b82f6, #818cf8);
  --success: light-dark(#22c55e, #4ade80);
  --danger: light-dark(#ef4444, #f87171);
}

Browser Support and Fallbacks

As of 2026, light-dark() is supported in Chrome 123+, Firefox 128+, and Safari 17.5+. For older browsers, provide fallback values:

.button {
  /* Fallback for older browsers */
  background: #2563eb;
  /* Modern override */
  background: light-dark(#2563eb, #1d4ed8);
}

Migration Strategy

  1. Add color-scheme: light dark to :root
  2. Replace @media blocks with light-dark() function calls in custom properties
  3. Test both themes using DevTools rendering tab
  4. Remove legacy @media blocks after verification
  5. Add JavaScript toggle support for manual theme switching

Conclusion

CSS light-dark() represents a significant simplification for dark mode implementation. By eliminating duplicate code blocks and letting the browser handle theme switching natively, developers can maintain cleaner stylesheets with less cognitive overhead. It’s a rare example where a new CSS feature simultaneously reduces code size, improves maintainability, and enhances user experience.