Skip to main content
← 🎨 CSS as a design systemΒ·Module B5 Β· Lesson 14
TaskBuild a card that uses light-dark() for its surface + text colors. :root sets color-scheme light dark. .card has padding 20px, background light-dark(white, oklch(0.22 0 0)), color light-dark(oklch(0.20 0 0), oklch(0.95 0 0)), border 1px solid light-dark(oklch(0.90 0 0), oklch(0.30 0 0)), border-radius 12px.

light-dark(): one color, two themes, zero @media

100 XP8 min
Theory

A 2024 CSS feature you already wanted

You've been writing this for years:

:root { --bg: white; }
@media (prefers-color-scheme: dark) {
  :root { --bg: oklch(0.18 0 0); }
}

light-dark() collapses both into one expression:

:root {
  color-scheme: light dark;
  --bg: light-dark(white, oklch(0.18 0 0));
}

The browser picks the first argument when the user is in light mode, the second in dark. The color-scheme declaration tells the browser "this element supports both" β€” required for light-dark() to know what "light" and "dark" actually mean.

Why this is better than @media

  • Less duplication β€” each token defined once, both values inline.
  • Local overrides β€” a single component can swap its dark color without redefining the whole root variable cascade.
  • Forced-scheme APIs β€” color-scheme: only dark at a subtree level still works; light-dark() reads the cascaded value.

Browser support

Chromium 123, Safari 17.5, Firefox 120 β€” all shipped 2024. Use a fallback for older Chromium:

:root { --bg: white; }
@supports (color: light-dark(white, black)) {
  :root { --bg: light-dark(white, oklch(0.18 0 0)); }
}
@media (prefers-color-scheme: dark) {
  :root:not(:has(*)) { --bg: oklch(0.18 0 0); }  /* legacy path */
}

For greenfield 2026 code, just use light-dark() and accept that pre-2024 Chromium gets the first arg always.

πŸ”’

Sign up to start coding

Theory is open to everyone. The interactive editor, live preview, and check are unlocked with a 7-day free trial β€” card required, cancel anytime.

Sign up β€” free trial β†’

First 10 lessons in each track are free. No card needed for those.

← PreviousNext lesson β†’

Get one Python or web tip a day β€” by email

Short, hand-written, no spam. Unsubscribe in one click.