# 0x0082.io

How I added dark mode to this website

People want dark mode on websites and in apps for different reasons. Some simply like how it looks, while others use it as an accessibility tool or to save power. With this post I will explain how I added dark mode to this website.

There are several ways to let the user toggle between light and dark mode. I want this site to use as few scripts as possible, so a script-based toggle button as suggested and used by many, is not optimal. Luckily, the MediaQueries Level 5 specification introduced the prefers-color-scheme media query, making it easy to add different styles for light and dark mode and then let the browser and OS handle the actual toggling. At the time of writing this post, this new media query is supported by the current versions of Safari, Firefox, and Chrome on both desktop and mobile. Neither Edge nor IE supports this feature, but I can live with that.

I use PostCSS to optimise my CSS files, with the postcss-custom-properties plugin to process CSS custom properties and replace them with their actual value. My initial thought was that I could add the media query for dark mode at the top of my stylesheet and redefine the color properties there, like this:

:root {
  --background: #fff;
  --color: #000;
}

@media (prefers-color-scheme: dark) {
  :root {
    --background: #000;
    --color: #fff;
  }
}

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

It seems, however, that postcss-custom-properties will only work withproperties declared in the main :root , and ignores properties declared or redeclared elsewhere. This means that the redeclaration of the --background and --color properties in the media query never gets processed.

Reading the issues in the postcss-custom-properties repository made me aware of another plugin, called postcss-css-variables . The main difference between these plugins is that the first restricts declarations of custom properties to the :root selector, as we noticed, while the latter allows properties to be declared inside any rule in whatever selector. Exactly what I need!

This functionality comes with a few cavecats , but as I will only be using this to redefine a few selected properties and use them in a single media query, these cavecats are not relevant for my usage. There is one small bug that affects me, though. Notice in the CSS below, how there are multiple media queries with only one rule each, even when they refer to the same selector:

body {
  background-color: #fff;
  color: #000;
}

@media (prefers-color-scheme: dark) {
  body {
    background-color: #000;
  }
}

@media (prefers-color-scheme: dark) {
  body {
    color: #fff;
  }
}

This is a known bug with no known (or easy) fixes, that has been reported multiple times . Luckily, the cssnano plugin for PostCSS, with the mergeRules optimisation enabled, will merge media queries into one per selector. Perfect!

After finishing everything up, the result is a website that automatically changes between light and dark mode according to the user’s own device settings.