Going Dark

Creating a dark mode (for this tiny personal site) was way easier than I thought it was going to be. It had been a loose idea of mine for a long time, but it wasn’t nearly as bad as I expected, and I shouldn’t have put it off. All told, it took under an hour.

This site is a mostly-cobbled-together collection of scripts, custom handwritten CSS, and a tiny sprinkling of JavaScript. That kind of bespoke code means it’s quite fun and easy to tinker with things, but changes can turn into nested layers of “fixes” pretty quickly.

The key things I wanted dark mode to do were these:

CSS variables are essentially the answer to “make modes easily interchangeable”. For whatever reason, I mistaken believed that CSS variables required a preprocessor like SASS or LESS; they don’t. The key is adding a light-mode or dark-mode class full of color variables to the html element, and then switching between these classes. For example, I now have classes that look like these:

.light-mode {
  --text-color: #333333;
  --body-background: whitesmoke;
  --primary-color: #2faced;
}
.dark-mode {
  --text-color: #bbbbbb;
  --body-background: #0a0a0a;
  --primary-color: #09547b;
}

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

This required a slight refactor, mostly just searching for colors (a #...... regex worked fine) and extracting them into light mode variables, then adding their dark mode equivalents. Now, when I manually added a dark-mode or light-mode class to the html element, I could see the modes switch before my eyes.

The answer to “don’t use a database” is “use localStorage”. If someone has their mode saved in localStorage, I use that to set the mode. However, I additionally wanted to respect people’s preferences upon the very first load of a page. For this, we can use window.matchMedia("(prefers-color-scheme: light)").matches to find whether someone prefers a light color scheme, otherwise set it to be dark.

Here’s some (un-minified) JavaScript to do that:

var mode = localStorage.getItem('mode')
document.getElementsByTagName('html')[0].setAttribute('class',
  mode
    ? mode
    : ( window.matchMedia("(prefers-color-scheme: light)").matches
      ? 'light-mode'
      : 'dark-mode'
      )
  )

Finally, I wanted a toggle that would allow people to change the scheme they preferred on the fly (and save their preference in localStorage). Another tiny piece of JavaScript attached to an icon:

var mode = localStorage.getItem('mode') === 'light-mode' ? 'dark-mode' : 'light-mode'
document.getElementsByTagName('html')[0].setAttribute('class', mode)
localStorage.setItem('mode', mode)

Try it out! This is a copy of the tiny lightbulb in the upper left: