Dark mode with CSS

Published Sun Mar 31 2019 17:03:32 GMT+0000 (Coordinated Universal Time)
In 2018, Apple brought us Animoji, and then dark mode. Now that the excitement of turning your own face into a chicken has subsided, we can get excited about a totally new way of thinking about UI.

State of the art

Webpages are getting the option too. Safari will soon pass a user’s colour-scheme preference to CSS. Firefox on MacOS, Windows and Android will support this feature too. It's all powered by a media query called prefers-color-scheme. MDN docs here.
. It's actually kinda easy to manage, as long as you rely solely on CSS for styling logic.
Here's a basic example:
.div {
  background-color: white;
  color: black;
  @media (prefers-color-scheme: dark) {
    background-color: black;
    color: white;
Yup - that's it. All you have to do is swap around your colours within the media query. Simple enough for most components.

Controlling dark mode

There are two use cases I can see for a more involved solution to the simple example above.
  1. Rolling out dark mode has to be all or nothing. You can’t leave your app in a state where some components support a dark-mode and others don't.
  2. You may also want to support other devices/browsers by implementing a custom solution that can be overridden.
I'm pretty sure both of these use cases can be easily answered with CSS-variables. Imagine the following:
.div {
  background-color: var(--background-color);
  color: var(--text-color);
  @media (prefers-color-scheme: dark) {
    background-color: var(--background-color--invert);
    color: var(--text-color--invert);
For use case 1, you can easily deploy your app with dark mode disabled by setting the --inverted variables equal to the regular ones.
For use case 2, you can easily allow users to flip the values of variables with the --inverted variants, and vice-versa.

A war of two worlds

Finally, what if you have some JS logic that determines how colours should be displayed. For example, you're setting a themeable colour variable within a React context.
The best way I’ve found to avoid duplicating logic between CSS and JS is to use getComputedStyle
to get the colour value being applied by CSS. That allows you to directly apply a value in JS that matches that already rendered on the page inside another tag, without needing to store the value in both mediums.


Most of my photos are licensed under Creative Commons BY-SA 3.0
If you are unsure about your right to use them please contact me.