The StartingUp Summer is also a good time to reopen our archives. After burying the questions AI made obsolete, here's one that survived: this 2021 piece on dark mode in CSS and respecting the theme your visitors chose hasn't aged a bit in substance. From prefers-color-scheme to the light-dark() function that arrived since, we're finally publishing it, warts and all, dusting off only the technical details.

Detecting your visitor's preference with prefers-color-scheme

macOS, Windows, iOS, Android: every system today lets its users choose between a light theme and a dark theme. The browser knows this choice and passes it to your stylesheet through the prefers-color-scheme media query.

This media query is now supported by every browser: you can rely on it with no second thoughts, in pure CSS, without a single line of JavaScript:

Forcing a theme with a toggle button

If you want to let your visitors manually switch themes with a button, you can force these values using CSS variables:

Taking dark mode further with SASS

If you use SASS, I can only encourage you to build yourself mixins to avoid duplicating these somewhat verbose directives all over your code:

Here I choose to add the class indicating the theme on the <html> element rather than on the <body>, so I can also style the <body> element with this mixin.

You'll then be able to easily roll dark mode into all your components inherited this way:

Dark mode: the ultimate level

We can take what we've built even further:

We realize there's no need to duplicate all the code for a light theme, which we could simply define as "the absence of a dark theme."

light-dark(): the shortcut that didn't exist in 2021

CSS has kept moving since these lines were written: the three major engines have supported the light-dark() function since 2024. It lets you declare both variants of a color in a single line, with no media query:

The color-scheme property is essential: it's what tells the browser your page handles both themes. It also gives you an almost free toggle along the way: force color-scheme: light or color-scheme: dark on the <html> element, and all your light-dark() values switch along with it. The function currently only applies to colors; for everything else, the media queries and variables covered above still apply.

Sources: