Implementing Dark Mode from the Beginning
Tags: css, webdev, html
Published: 2019-12-26 14:21:06

*__This post was originally published on [timothymiller.dev](https://timothymiller.dev/posts/implementing-dark-mode-from-the-start/)__*

Dark mode. Possibly one of the most developer-flexy features of 2019, now available for viewing on my personal website.

Before I talk about Dark Mode, I need to talk about the shoulders I’m standing on: I first read about implementing Dark Mode on CSS-Tricks from the indomitable [Robin Rendle](https://css-tricks.com/dark-modes-with-css/).

I also read about _designing_ for dark mode on [Stuff & Nonsense](https://stuffandnonsense.co.uk/blog/redesigning-your-product-and-website-for-dark-mode) (best name ever) by Andy Clarke.

Those two articles are the main inspiration for this article. With that said, let’s talk dark mode.

## Why Does This Exist?

First of all, Dark Mode is the “common” name for a feature popularized[*](https://dev.to/moopet/comment/jbo1) by Apple, more technically referred to as the “prefers color scheme” media query. Introduced in [Media Queries Level 5](https://drafts.csswg.org/mediaqueries-5/#descdef-media-prefers-color-scheme), this media query allows people to specify if they prefer light backgrounds or dark backgrounds. And it allows us, as website creators, to define what our website looks like with a light or a dark background.

Dark Mode is intended for use as an accessibility feature, and it’s important to remember that. There are certainly other reasons to use it—I use it, and I have no disability that forces me to do so—but we need to remember that this is an _important accessibility tool_ and we shouldn't hijack it for our own purposes. Dark mode should make our websites easier to browse at a lower contrast, it should _not_ be a flashier, edgier, “darker” version of our website.

## Implementation

Implementing “dark mode” is fairly simple, but it has far reaching implications all across your website. There _are_ simple ways to do this,
such as cleverly using a [blend mode](https://dev.wgao19.cc/sun-moon-blending-mode/), but there’s only one way to make sure your colors are accessible _and_ look good, and that’s by developing your own theme, using your own hand-picked colors.

For me, my site only had a couple of colors, so this wasn't difficult. I took the blue that I’ve been using for links, and used that for my background color. Then I _substantially_ lightened that same blue color, and used it for links.

Simple:

![An image demonstrating the differences between dark and light modes.](https://thepracticaldev.s3.amazonaws.com/i/jw5voda4gb9jydw1ktfq.png)

Next up, readability.

You may have noticed in the past, when visiting websites with dark backgrounds, that white text on a dark background is hard to read. It starts to look "fuzzy" after a while, where your eyes have trouble focusing on the text. This is generally just a _contrast_ issue,
light text on a dark background generally appears higher contrast than the reverse. So you want to _lower_ your contrast ratios in dark mode,
because again, this is an accessibility feature for people looking for _lower contrast_. It is a tricky line to walk though, and you need to make sure your contrast isn’t _too_ low, so you’ll want to use a tool to help you. Lea Verou’s [Contrast Ratio tool](https://contrast-ratio.com/) is perfect for this, or if you want more info, WebAim has a [nice tool](https://webaim.org/resources/contrastchecker/) as well.

For accessibility sake you always want at least a 1:5 ratio for text. But for dark mode, you also don’t want that number to go too high, I would say somewhere between a 1:5 and a 1:15 is going to give you the best results. So I'll quickly run through the colors I have chosen:

```css
/* dark mode colors: */
--background: #082840;
--text-color: #FFFFFF; /* contrast ratio: 15.13 */
--link-color-normal: #75BAED; /* contrast ratio: 7.2 */
--link-color-visited: #5EAFEA; /* contrast ratio: 6.33 */
```

Contrast-wise I'm looking good, although the plain text color is still a little high for me. I'll try an off-white:

```css
--text-color: #E0E0E0; /* contrast ratio: 11.46;
```

I like it. The off-white looks a little “softer”, easier on the eyes.

Now implementing dark mode should involve more than just colors. I also made a few typographic changes for dark mode. Using a light-weight font makes a _huge_ difference when it comes to dark mode, and I also added a tiny bit of word spacing, just to give things a little more breathing room.

Here’s what those changes look like:

![An image demonstrating the differences between dark and light modes.](https://thepracticaldev.s3.amazonaws.com/i/bsl1uwtxv929i5d0vzxs.png)

Looks like pretty minor stuff, but it makes a _huge_ difference, especially when reading long articles.

## Varinception!

To wrap it all up, here’s how I structured my CSS variables. I like to be able to use _real_ color names for my variables—I’ve never been a big fan of the `--primary-color` and `--secondary-color` school of thought—but I also wanted logical names for links, headings, copy fonts, etc. So I ended up using _variables in variables_, a concept I affectionately call varinception.

Here’s what that looks like:

```css
:root {
    /* add all colors up front */
  --red: #C5004A;
  --darkred: #7F0036;
  --lightgray: #e0e0e0;
  --gray: #C0C0C0;
  --darkgray: #333;
  --navy: #17050F;
  --blue: #082840;
  --mid-blue: #5EAFEA;
  --light-blue: #75BAED;
  --white: #E0E0E0;

  /* varinception! */
  --text-color-normal: var(--darkgray);
  --bg-color-normal: var(--white);
  --link-color-normal: var(--blue);
  --link-color-visited: var(--navy);

  --header-weight: 700;
  --header-line-height: 1.5;
  --header-letter-spacing: 0;

  --copy-weight: 400;
  --copy-line-height: 1.5;
  --copy-word-spacing: 0;
}

@media (prefers-color-scheme: dark) {
  :root {
    /* I find this readable and easily maintainable */
    --text-color-normal: var(--white);
    --bg-color-normal: var(--blue);
    --link-color-normal: var(--light-blue);
    --link-color-visited: var(--mid-blue);

    --header-weight: 700;
    --header-line-height: 1.7;
    --header-letter-spacing: .025em;

    --copy-weight: 100;
    --copy-line-height: 1.7;
    --copy-word-spacing: .05em;
  }
}
```

Using these variables now requires very little code:

```css
html,
body {
  color: var(--text-color-normal);
  background-color: var(--bg-color-normal);
}
h1,h2,h3,h4,h5,h6 {
  font-weight: var(--header-weight);
  line-height: var(--header-line-height);
  letter-spacing: var(--header-letter-spacing);
}
p {
  max-width: 37.5em;
  font-weight: var(--copy-weight);
  line-height: var(--copy-line-height);
  word-spacing: var(--copy-word-spacing);
}
```

And that's that! We now have a fully function and _accessible_ dark theme for our website.