Dark mode is becoming more popular for not just apps, but websites as well. So much so that it can be found on some of the most popular websites across the web. Adding a dark mode is not a feature exclusive to large teams and does not require rebuilding your sites from scratch. With a bit of planning, you can add dark mode to any site or PWA.
Part 1: The Setup
With those created we have a good foot forward for success. Let's set the content for our demo website. Copy the code below. I will separate the file name from the content with three dashes for simplicity.
This is a fairly basic page and we will cover more complexity, but the prinicples we'll cover work across the board once we lay the groundwork here.
The style here uses css selectors based on the data tag 'theme'. in HTML this looks like <html data-theme='light'>. Please note the lack of space between html and the opening brace [ . That is important for targeting and won't work reliably otherwise. If you're having issues getting the theme to switch, check here first. We could instead use CSS classes if we want, but data attribute changes provide a targeted approach we can apply across the board.
Two functions are used for swapping between light and dark theme here by simply applying the data tag for the theme we want. The dataset property is one of the great new features in HTML5 that simplifies working with data-* attributes on page elements. If you have questions Mozilla has a great breakdown to read more about it here. You can trigger these functions manually at the moment by calling them from the developer tools debugger console in your browser.
Part 2: The Complexity Problem
The next step is to make a more concrete example based on what we've done so far. First we're going to modify the index.html file to add more diverse content that better represents what we may have on an average page.
Once you've made the change, refresh the index page in your browser and see the difference. Make sure it loads properly and doesn't have any weird tags hanging out, then we need to update the CSS so the page has the basic styling we want from our new layout.
These CSS changes set up the basics of a page with a main background color, content background colors, and varied header colors.
Add the following lines to the top of the code.js file and don't delete the other lines. Just add these above the previous ones.
With these lines added we're doing quite a bit more, so let's step through this code for a moment.
Line 1 window.addEventListener(...) : First we're adding an event listener to the window's load event. This will call our themeBootstrap function once the window is finished loading, but before all the content itself for the document completes displaying (i.e. images may not be done loading before this is called).
Line 3 function themeBootstrap() : The bootstrapper is a stripped down function where we can call all of our event listeneres that we want to be related to the theme. In this case, we add another listener that monitors the select box we've added to the top left corner of the page so when the selection is changed it will automatically call the updateTheme function.
Line 7 function updateTheme(): Here we're finding the selected option for the select box and passing the value property into a switch, which then calls the appropriate function based on the user's selection. It's important to note we're calling toLowerCase() on the value so the values match the cases, since 'Light' and 'light' are not the same string values.
At this point when you load the page up you should be able to change the theme using the select box in the top left corner. The result however is horrible and unusable, since colors don't work, at all. In the following steps we're going to fix the CSS file to centralize our colors using CSS Variables or Custom Properties (they're the same thing). For more a more in-depth look at them check out the Mozilla foundation's info here.
First, let's move the color values we want for our primary theme to the top light theme, and also update the main light theme also target the html page in the event that we don't have a theme selected by just adding another selector.
next we update the color and background values throughout the page to use these variables by wrapping the variable name in var()
The above changes the values so they're bound to our new variables, and allows us to adjust them in dark mode. Now we can override these variables when we change to dark mode in one place, without having to update everything and chase a ton of values in a bunch of different areas.
With the above change we're modifying the values to make everything more viewable and attractive to our readers while also shifting the background colors to a darker shade. There is another benefit to moving the colors up to these two themes and using variables everywhere else: we can add new color themes with very little effort in the way of code. You just add another theme layout in your theme section, then add the appropriate dropdown menu option to match, and add the appropriate fix to the JS.
Part 3: Remember Me?
The first update adds a call to loadTheme() so we can call the selected theme once the page loads. The second update adds loadTheme(), which first gets the value from local storage (or applies 'light' as the default should there not be a saved theme). Then it grabs the select box and finds the matching option to the theme, sets the index for the select box to that option (so the select box always shows the properly selected value on initial load), and then calls updateTheme() to manage the actual theme load.