Timothy Miller

Implementing Dark Mode from the Beginning

Dark mode. Possibly one of the most developer-flexy features of 2019, now available for viewing on this very 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.

I also read about designing for dark mode on Stuff & Nonsense (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 pioneered by Apple, more technically referred to as the “prefers color scheme” media query. Introduced in Media Queries Level 5, 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, 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.

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 is perfect for this, or if you want more info, WebAim has a nice tool 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:

/* 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:

--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.

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:

: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:

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; /* 600px /16 */
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.

← Home