Create a dark mode with CSS Variables

February 2022: I finally got around to implementing this method for real.

Apple introduced “dark mode” for macOS in late 2018 and now designers are scrambling to duplicate the look everywhere. While Apple’s own guidelines say dark mode is strictly an aesthetic preference, others cite benefits like improved battery life and reduced eye strain.

I'm currently dabbling in my own site redesign, and trying to decide on an updated colour scheme. I was curious how complicated it would be to create my own dark mode.

It turns out: not that hard! But the approach I took relies on CSS Variables, so it requires a little planning. In this post I’ll explain how this minimal proof of concept works. You can skip to the final product if you like.

1. Define the defaults

I’m going to use light mode as the default, and let people switch to dark mode if they want. That means setting the default colours on the :root element:

:root {
--colorBackground: white;
--colorForeground: darkslategray;
--colorLink: royalblue;
--colorCode: crimson;
}

For now, I'm setting colours for just a few elements. (For a more complex project, this list would surely get longer.)

2. Define a dark colour scheme

The benefit of CSS Variables is right there in the name: they’re variable. You can change them any time and everything that references them changes too. So what we’ll do is change the colour values when they’re inside an element with a .dark class:

.dark {
--colorBackground: black;
--colorForeground: lightgray;
--colorLink: cornflowerblue;
--colorCode: lightpink;
}

3. Use those colour variables in your design

Now you simply use these variables in the rest of your CSS. Instead of, say, color: darkslategray, you’d write color: var(--colorForeground). For example:

body {
background-color: var(--colorBackground);
color: var(--colorForeground);
}

That may feel more cumbersome, but it allows you to be flexible in other ways. Want to change your look a year from now? Just edit those colour names.

In this case, the important part is that it also lets you use code to change colours on the fly.

4. Add a dark mode toggle

We already defined our dark mode colour scheme above. To activate it, we just have to add that .dark class to the page. Since it’s an on-or-off value, let’s use a checkbox to toggle it on or off:

<input id="darkmode" type="checkbox" /> <label for="darkmode">Dark Mode</label>

By default, it will start unchecked. With Javascript, we can tell when that changes and toggle the .dark class on the page’s <body> element:

// Find the checkbox by its ID
document.querySelector("#darkmode")
// listen for when the box’s “checked” status changes
.addEventListener("change", function(event) {
// toggle the `.dark` class on the page’s body element
document.querySelector("body").classList.toggle("dark");
});

And that’s it. When the user checks that box — either by tapping on a touch screen, clicking with a mouse, or navigating with their keyboard — the colour scheme switches to dark mode.

The full working example

This Codepen demonstrates the principle in full.

See the Pen Simple Dark Mode with CSS Variables and 3 lines of Javascript by Graham F. Scott (@gfscott) on CodePen.

Getting more elaborate

This is a very minimal example, but you could extend it in several ways:

  • Use cookies to remember the user’s choice of colour scheme.
  • Create more complex checkbox styles.
  • Offer more than two looks by changing the <body> class to, say, high-contrast or bubblegum.
  • Use a media query to detect the user’s preference at the OS/browser level.

Notes and caveats

  • Support for CSS Variables is pretty good. As always, use progressive enhancement to make sure your site remains accessible to everyone.