Easy Light and Dark mode switching with CSS

Published on 04 July 2020 3 minute read (578 words)

Add automatic dark mode switching to your site with the magic of CSS variables. Be one of the "cool kids" without working too hard to do it.

If you haven't lived under a rock over the past couple of years, chances are high you've heard of dark mode, and the "dark mode all the things" craze going around. Hell, you might be reading this whilst using dark mode.

It's certainly been a very useful accessibility/preference feature, however, I'll be the first to admit that not a lot of implementations of it are all that great. A lot of them lack the necessary contrast and some of them straight up blind you needlessly. Enough ranting — you have a fancy shmancy site and you want it to have a dark mode theme for it, you want it now, but you don't want to do all that JavaScript stuff to get it, and you want the switch to be instantaneous and happen automatically. You're in luck.

Enter CSS variables

There's a cool CSS feature, called "Custom properties" (everybody calls them "Variables" which they are), that'll help us achieve your goal of dark mode domination.

It allows us to set a global set of variables, use them as needed in our CSS, and override them on demand — dynamically, and straight out of the box, no pre-processors, no nothing. When I started doing this for a living some 15 years ago, I would've never dreamt of the ability to do this straight in the browser without a pre-processor of some kind.

It's really easy to use. We define some variables (typically) on top of our .css file as such:

:root {
  --black: black;
  --white: #FFFFFF;
  --chonky: 4rem;
}

Then, somewhere else, we get to use them via the custom property function var():

.myRandomDiv {
  background-color: var(--white);
  color: var(--black);
  font-size: var(--chonky);
}

The :root CSS pseudo-class matches the root element of the tree that represents our HTML document. In HTML, :root is essentially the <html> element, except that its specificity is higher.

Variables are defined with the custom property notation -- and can be equal to any valid CSS value, and be reused anywhere else in the document once defined.

Since they are part of CSS, they are also subject to cascading, meaning we can override them just as easily, depending on the context:

.myRandomDivsParent .inverted {
 --white: black;
  --black: #FFFFFF;
  --chonky: 1rem;
}

This is exactly the mechanism we're going to exploit to get our super-simple dark mode.

Detecting dark and light modes

Much like most browser features, you can also detect the user's preferred colour mode via a CSS media query:

/* This will make our background white for light mode */
@media (prefers-color-scheme: light) {
  body {
    background-color: white;
  }
}

/* This will make our background black for dark mode */
@media (prefers-color-scheme: dark) {
  body {
    background-color: dark;
  }
}

Adding light and dark mode switching to our site

So, let's use all we've learned so far and make our site switch on demand, shall we?

First, we need to set up our variables, those will need to be used throughout our site, to make the switch automatic:

:root {
  --background: white;
  --textColor: #000;
}

Then, right after, we need to define our variables' values for dark mode:

@media (prefers-color-scheme: dark) {
  :root {
    --background: #111;
   --textColor: #FAFAFA;
  }
}

Now, we need to use our variables in the website. Here's an example:

body {
  background-color: var(--background);
  color: var(--textColor);
}

And the end result:

That was it! No, really, that's how simple it is.

Debugging dark and light mode

Now, you probably wouldn't want to constantly have to switch your OS/browser between light and dark mode to develop and debug your CSS. Browsers, such as Chrome, have you covered

Colour scheme Dev tools demo

Browser support

Naturally, as is with all nice things — this feature is only supported by modern browsers, so you might need to implement some fallback values if you care about legacy browsers like IE11. (you don't, but your inane clients might, I know)

CanIuse report on CSS Custom properties

Conclusion

I hope this article helped you in some way, and please, do let me know what you think about it on Twitter :)