Website Redesign: Re-thinking Dark Mode

March 7, 2020

One of the first enhancements I knew I wanted to make to this site when redesigning it was to add a dark mode. It’s not that difficult to do at a base level and is something that I personally enjoy at both an OS level (e.g. iOS dark display) and on other sites that support it.

But swapping colors based on user settings is only the tip of the dark mode iceberg. I’ve been re-thinking the way it’s implemented around here and thought I’d jot down a few of the things bouncing around in my head as well as some lessons I’ve learned so far.

Let’s re-visit what it means to be “dark mode”

It’s hard to imagine someone who is unfamiliar with the concept of dark mode. It’s been around for at least the last year as Android phones adopted it early in 2019, but probably gained more widespread attention when iOS introduced it later in the year and with official support for it on the web introduced in Safari 68 last October.

By “support” I’m referring to a new CSS media query that allows developers to tap into a user’s OS settings to detect whether the user prefers a light or dark display.

iOs Display & Brightness settings

That media query? It looks like this:

@media (prefers-color-scheme: dark) {
  /* Styles for users who prefer dark mode */
}

@media (prefers-color-scheme: light) {
  /* Styles for users who prefer light mode */
}

So that’s what we’re dealing with when we talk about “dark mode” on the web. We have a means of knowing how a user prefers to view content on their device and adapt our own UI accordingly. It’s not an especially difficult concept to grasp but — like many things in web design and development — there’s a lot of nuance that’s worth understanding and taking into consideration when implementing it.

What dark mode can (and probably shouldn’t do)

It’s easy to think of dark mode as a technique for swapping colors on a website. The user prefers a certain type of display on their device, so we respect that setting on our sites. Seems like a win-win, right? The user gets to control their own experience in both apps and websites, creating a consistent look and feel throughout.

But we’re probably short-changing the feature if we only look at it as a way to respect the color settings of a device. For example:

  • Does the user prefer dark UI everywhere or only in apps?
  • Is the user’s preference based on aesthetics, or making content more accessible?
  • How dark is dark? Is black always the best replacement for white, or are there other color combinations that either serve the purpose just as well or even better?
  • Is color the only part of the UI that needs to be respected? It’s easy for our minds to go straight to color when we’re working with terms like “light” and “dark” but what we’re really talking about is contrast — and that transcends color.
  • What’s the browser support for the media query? Are some users going to lose out and, if so, how do we accommodate them?

Yes, there’s a lot of nuance there and there’s probably a lot more that could go in there.

Dark mode browser support

The last point in that list is worth briefly calling out. I mean, what’s the point of designing for dark mode if, um, no one can experience it?

Well, that’s not the case. The media query is very well supported at the time of this writing. All major browsers support it (excluding Internet Explorer and a few minor mobile browsers) with 80.85% global coverage, so it’s most certainly ready for prime time — and the specification for it is still unofficial!

The future of dark mode may be impacted as the specification evolves. For example, there is another CSS property in the proposal to force a color scheme, essentially opting fully out of color preferences at the OS level and instead maintaining full control of the CSS.

My current approach to dark mode

At this very moment, I’m employing dark mode using the media query at the :root level. I’ve created a number of custom properties that combine into a palette of baseline colors I use throughout the design of this site.

:root {
  --red: #fd1e1e;
  --orange: #fd5a1e;
  --gray-lightest: #f7f2f1;
  --gray-lighter: #e8e3e1;
  --gray-light: #cabdb9;
  --gray: #b0a3a0;
  --gray-medium: #7d7472;
  --gray-dark: #615a58;
  --gray-darker: #463e3b;
  --gray-darkest: #221d1b;
  --white: #fff;
  --black: #000;
}

Then I assign those colors to other custom properties with more functional names:

<pre class="language-css"><code>:root {
  /* Same as before */
  --primary-color: var(--orange);
  --text-color: var(--gray-darkest);
  --background: var(--gray-lightest);
  --site-title: var(--gray-dark);
  --border-color: var(--gray);
  --link-border: var(--primary-color);
  --link-color: var(--text-color);
  --link-hover: var(--white);
  --link-current: var(--gray-darkest);
  --code-blocks: var(--white);
  --error-color: var(--red);
  --table-background: var(--gray-lighter);
}</code></pre>

Finally, I re-assign those colors based on the user’s preference using the media query:

@media (prefers-color-scheme: dark) {
  --text-color: var(--white);
  --background: var(--gray-darkest);
  --site-title: var(--gray);
  --border-color: var(--gray-lightest);
  --link-color: var(--white);
  --link-current: var(--white);
  --code-blocks: var(--black);
  --table-background: var(--gray-darker);
}

That’s a valid and legit approach. It’s also the cheapest, easiest way to go.

But I’d be lying if I said that’s all there is to it because now this opens up additional questions I have yet to answer:

  • Should the user be able to toggle between light and dark mode?
  • Are the contrasts of my dark mode color combinations equally or more compliant

Why can’t anything in front-end development be easy? 😝

What I’ve changed since last time

This is already my second time addressing dark mode in the redesign of this website. The first time walked through my initial implementation, but didn’t really go into UX considerations… or really anything beyond scratching the surface of how I did it.

Since then, I’ve been focusing on the color palette itself. One of the questions I asked earlier is whether black and white are always perfect replacements for one another. My answer for that is a resounding no. Black text on a white background is a lot different than white text on a black background. Again, since we’re talking more about contrast than color, there are varying degrees of contrast. In other words, replacing one high contrast with another may not be the best user experience. Maybe it’s better to swap a high contrast with a medium contrast. The W3C has a nice definition of this with examples worth checking out.

So, what have a I changed? Well, my “dark” mode is now a much lower contrast than it was. After using dark mode myself on my own site for a few weeks, I determined — based on my own experience, of course — that white text on a near-black background was taxing on my eyesight. I’m far-sighted and found my eyes feeling sore after reading a couple of my own posts. I decided to “design” a softer appearance that removes blacks and grays altogether In favor of muted earth-tones and a less intense primary color.

My homepage before the change.
The UI is a lot “softer” this way.

Where to go from here

I probably need to continue refining the palette a bit, but I’m pretty comfortable with it in general. This palette is actually a lot closer to a design iteration I really liked but ended up abandoning, so I feel like I got to sneak in a second design of the site.

The new palette opened my eyes to contrast in general, so I’ll probably re-visit my “light” mode palette as well. The high contrast is pretty strong and might need to be lowered a notch to encourage more reading. This is a blog, after all.

I also want to address the whole UX consideration of letting users toggle between light and dark mode. It’s not a trivial enhancement, but it’s certainly doable with a little effort… and JavaScript.

I also want to consider more things. How about images? Should the contrast be muted there as well? Is the change from light to dark jarring? It is, so I’m glad there isn’t a way to toggle between them at the moment!

So, more to come. Just a little housekeeping since the last time I checked in.