How to Implement Device Setting-Aware Dark Mode in Modern Web Apps

Android 10 and iOS 13 both introduced a device-wide dark mode setting user can turn on and off at will. Last month we explained how to toggle the mode on and off on iPhone and Android devices. Note that light/dark setting is not limited to phones alone ‐ it can apply to any device that supports it, including desktop and laptop computers.

The intent is when use switches to dark or light mode, all apps on that device also switch accordingly.

In the past the dark mode setting has been managed at application level and had to be set individually for each app. This old approach requires a lot of effort from any user wanting to change the setting across multiple apps.

With the introduction of device-wide dark mode, a user can now - ideally - make one change in settings and all apps change simultaneously.

This works as long as these apps have been updated to be aware of the device’s dark mode setting.

In today’s tutorial we will discuss how to handle swapping CSS colors for a modern web application, so that no matter which mode user has selected, the web application can accommodate the user’s preference!

Code Sample Explained

In this tutorial we will adopt a strategy from Android world and apply it to a web application. We will do this because it greatly simplifies the process of changing colors on the fly. We will accomplish this by utilizing CSS variables. This will allow us to define colors once, and then refer to those colors using variable names.

Step 1: Define light mode colors

Define these colors inside :root. Notice the syntax for each color is two hyphens followed by variable name, colon, the actual color value, and semicolon. In this example we are using HEX colors, but feel free to any CSS-supported style of defining color values.

1
2
3
4
:root {
--body-bg-color: #EEE;
--body-text-color: #000;
}

Step 2: Define dark mode colors

Next let’s define a value for each color when the device is set to dark mode. Notice we will have to override every color we defined in step 1 if we want it to change when device is set to dark mode.

Another point to notice is this time we will wrap :root inside a media query using prefers-color-scheme: dark. This means these colors will only be used if and only if the device is set to dark mode. You can read the technical specs for prefers-color-scheme here.

1
2
3
4
5
6
@media (prefers-color-scheme: dark) {
:root {
--body-bg-color: #000;
--body-text-color: #fff;
}
}

Step 3: Use variables in stylesheet

So far we have only defined the values for different color variables, not actually used them. The last step is to actually use these variables. For example, to set the body background and text color, we would do the following:

1
2
3
4
body {
background: var(--body-bg-color);
color: var(--body-text-color);
}

Notice when using CSS variables, we use keyword var() and the name of the variable inside the parenthesis.

By using variable names for all colors, it is easy to switch between color themes and to refer to the colors throughout the rest of the stylesheet. If you ever want to change your colors, no need to update every reference. Only change the value of the variable you defined inside :root.

Complete Code Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* Default to lite mode */
:root {

/* Specify all color variables */
--body-bg-color: #EEE;
--body-text-color: #000;
}

/* Dark mode */
@media (prefers-color-scheme: dark) {
:root {

/* override colors that need to change
when dark mode is active */
--body-bg-color: #000;
--body-text-color: #fff;
}
}

/* use the variables */
body {
background: var(--body-bg-color);
color: var(--body-text-color);
}

Happy Coding!

If you enjoyed this article please share it with your friends!

Mobile First