Adding dark mode to your web app
Tags: webdev, css, tutorial
Published: 2022-02-11 04:37:57

Dark mode in applications and web sites is a feature that seems to be becoming increasingly popular and ubiquitous. Thankfully, adding basic dark mode support to an existing web app that uses CSS is a fairly straightforward process. If you want to get started quickly, you've come to the right place! For a more in-depth and comprehensive guide, see the [Complete Guide to Dark Mode on the Web](https://css-tricks.com/a-complete-guide-to-dark-mode-on-the-web/#aa-using-custom-properties).

## The Demo App

We will be adding dark mode support to a very simple web page that is backed by the following HTML code:

{% gist https://gist.github.com/bhaeussermann/b6f128c5aa76a57da6b95f80ffaae38f %}

Here is the CSS used to style the "item" elements:

{% gist https://gist.github.com/bhaeussermann/4e5f4712c5dc42dd52742815cd513679 %}

Here is what the page looks like in the browser:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xvi9j0me48x7zgov2wna.png)
 
To test how well we are doing in dark mode out of the box, I first ensure that my browser's theme is set to the system theme (or "auto"), meaning it will display everything in light or dark mode based on the operating system setting (this should be the default). I then change the operating system setting to dark mode (assuming it was in light mode to begin with). The browser's window border changes accordingly, but the web page does not. We've got some work to do.

## Adding basic dark mode support

The quickest first step we can take is to tell the browser that our page supports both "light" and "dark" colours by adding the "color-scheme" meta tag to our page's `<head>` element:

{% gist https://gist.github.com/bhaeussermann/57562c1aa7acfbe6ace4985938d5d614 %}

Refreshing the page reveals that we've already achieved a result:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w6699vk2rrux22e1c7hl.png)
 
The elements that didn't have colours assigned to them in the CSS now take on the browser's default colours for the dark theme. This wasn't happening before because in the absence of this tag, the browser will assume that the page supports light mode only. Other benefits to specifying the "color-scheme" meta tag are:

- unstyled controls (e.g. text boxes, check boxes) will also have their appearance changed to correspond to the selected theme, and
- even if all colours are specified in CSS, having this tag set will prevent a white background with black text flashing while the CSS is downloaded and the dark colours finally applied.

Adding the "color-scheme" meta tag was a quick win, but there's clearly more work left to do: we need to define the dark mode colours for the elements that do have their colours set via CSS. Colours in our CSS code can easily be overridden for dark mode using the `prefers-color-scheme` media query:

{% gist https://gist.github.com/bhaeussermann/1ece8518bc858b2dfa6f941599c5ab88 %}

 We're looking much better now!

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wfqvbe1lttkv3sme6yvl.png)
 
*A quick way to find "dark mode" colours is to simply invert all the regular colours. This can easily be done by opening the Calculator, switching to Programmer mode and selecting hexadecimal. Then simply calculate `FFFFFF - xxxxxx`, where `xxxxxx` represents the regular colour in six hexadecimal digits. This provides a good starting point from which to tweak the colours further.*

## Making things managable

On anything but the smallest of applications, CSS code like the above will prove to be a lot of work to maintain with future styling changes. Moving all the colours into CSS variables and setting these variables based on the current theme will result in more tidy CSS code that is much easier to maintain:

{% gist https://gist.github.com/bhaeussermann/da7c6dba47ca6879717bcfbae6ce8526 %}

By keeping the declaration of all the variables at the top of the file, the colours can now all be changed in one place.

## Dealing with images

If your web app features images, they may not fit in well with the dark colours. In this case you may want to create a "dark" version of each image to use instead. If the image is specified in HTML, then switching between the "light" and "dark" version can all be done in HTML by replacing:

{% gist https://gist.github.com/bhaeussermann/902739e3852dbc5c012ceba3350dd2a5 %}

with

{% gist https://gist.github.com/bhaeussermann/9c57eae9e80c6998b7064ee456b5b99e %}

## Allowing colour scheme selection via in-app setting

There may be scenarios where a user would want to override the colour scheme in your app, regardless of their operating system setting. In this case you may want to offer options for "light", "dark", and "auto" (which would use the operating system setting).

At this point CSS alone won't cut it; we're going to have to use some JavaScript to achieve this kind of functionality. One way would be to have the JavaScript code add or remove some special class (say `dark`) to the top-most HTML element and set up the CSS rules to react to that:

{% gist https://gist.github.com/bhaeussermann/0ed53fe5b0201292b6785fe612d1dbd6 %}

The JavaScript code would have to watch the `prefers-color-schema` media value in case the user has "auto" selected. The underlying code may look something like this:

{% gist https://gist.github.com/bhaeussermann/65c53265dc6f9b255f7fc6b2de93b476 %}

## Conclusion

Accessibility and improved battery life are just some of the reasons why dark mode has become a popular alternative colour scheme on most of the operating systems we use today; not to mention, it's hip and trendy 😎. Thankfully, implementing dark mode in web applications almost couldn't be easier, so why wait? 😉