🠐 Jeremy Bernier

Why I Don't Put My CSS Inside the Dark Mode Media Query

Today on the front page of Hacker News was a post titled "Dark mode in CSS with prefers-color-scheme" where the author implements dark mode using the prefers-colors-scheme media query which reads from the user's operating system preferences.

I love dark mode, and love that there is finally a media query for it (available since June).

@media (prefers-color-scheme: dark) {
  // CSS goes here

The problem is that putting your CSS inside the media query makes it difficult to enable the user to toggle dark mode on/off.

If a dark mode user decides they'd rather use your app in "light" mode, then you now have the problem of having to override all of the dark styles set inside the media query. Since media queries have high priority in the pecking order of CSS hierarchy, this is not actually simple, and ultimately requires duplicating your light theme CSS into some CSS class (eg. `.theme-light`) that you dynamically add to the body. Having your site's CSS written in two places is of course a maintenance nightmare and very inefficient.

You have the same problem when a "light" mode user (by OS setting) tries to switch to dark mode. They'll never be able to access that media query without changing their OS setting, and so you'll have to do the same thing in reverse - dynamically add a CSS class to the body (eg. `.theme-dark`) and duplicate all of your dark theme CSS that you wrote in your media query into this `.theme-dark` class.

Obviously it 's a horrible idea to duplicate any code, let alone duplicating twice.

I opted out of using the media query and settled with simply reading the media query tag with Javascript, then dynamically adding a .theme-dark class if prefers-color-scheme: dark.

const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches
if (isDarkMode) {

Of course one would do well to extend this further and save/load the user's dark mode preferences (eg. via localStorage) .

I would've preferred the pure CSS solution of simply using the media query, so it's a shame we have to rely on Javascript here, but user experience and control should always be the #1 priority.

Note: You can toggle dark/mode on/off on this website via the button on the top right of the page. I implemented it the same way as described in this article, except I default to dark mode :).