In the previous post, I explained how to automatically set a dark theme using a CSS media query to check site visitors’ device preferences. Next, I will show you how to let users select the site theme by writing a few JavaScript functions.
You can also check out the demo if you wish to skip the whole guide to show you how it works.
If you have followed the guideline to set a dark theme before, you will notice that we’ve used a :not()
pseudo class to apply the dark theme when the :root
element doesn’t contain data-site-theme
as the attribute.
@media (prefers-color-scheme: dark) {
:root:not([data-site-theme]) {
--theme-color-background-400: #{$color-charcoal-400};
--theme-color-background-500: #{$color-charcoal-300};
// The rest of the dark theme color tokens
}
}
In HTML, :root
represents the <html>
element and is identical to the selector html, except that its specificity is higher.
Building a feature for site visitors to select the site theme requires a few requirements:
- We need a way to store the user option.
- We need to add some UI elements for users to choose their preferred themes.
- We need to update the CSS to support the selected theme.
Creating Theme Manager Object
One of my favorite design patterns is Command Pattern, where you can encapsulate actions as objects. For me, it’s the most straightforward design pattern to learn if you’re new to JavaScript because you’re only dealing with methods to manipulate DOM. For example, here is the complete snippet to create a theme manager that I use on my site.
const _manager = {
setTheme: function (userColorScheme) {
localStorage.setItem('user-color-scheme', userColorScheme);
document
.querySelector('html')
.setAttribute('data-site-theme', userColorScheme);
},
setUserTheme: function () {
if (
localStorage.getItem('user-color-scheme')
) {
let userColorScheme = localStorage.getItem('user-color-scheme');
this.setTheme(userColorScheme);
}
},
resetUserTheme: function() {
localStorage.removeItem('user-color-scheme');
document
.querySelector('html')
.removeAttribute('data-site-theme');
},
init: function () {
this.setUserTheme();
},
};
_manager.init();
Let’s take a look at each method and its purpose.
setTheme
setTheme
is the method to set the site theme. Using the built-in localStorage
property, you create a local item called user-color-scheme
with the value you pass into the method. You then use querySelector
to apply the value to data-site-theme
attribute. Here is an example of an updated html data attribute when you run _manager.setTheme('dark')
.
<html lang="en" data-site-theme="dark">
Checking Local Storage — You can check the local storage value using Chrome Dev Tools and navigate to the Application tab. If you’re using Safari, you can check the Storage tab.
setUserTheme
The method’s purpose is to apply a user theme if we detect any values in the local storage. First, we check if there is any value by using localStorage.getItem('user-color-scheme')
function. If we detect a value, we will apply them by calling the setTheme
method we built earlier.
setUserTheme: function () {
if (
localStorage.getItem('user-color-scheme')
) {
let userColorScheme = localStorage.getItem('user-color-scheme');
this.setTheme(userColorScheme);
}
}
resetUserTheme
Although it’s nice to allow site visitors to choose their theme, we also need to offer a way for them to reset their choice for the site to match their device’s theme setting. This method will remove user-color-scheme
item from the local storage and remove the data-site-theme
attribute from the document.
resetUserTheme: function() {
localStorage.removeItem('user-color-scheme');
document
.querySelector('html')
.removeAttribute('data-site-theme');
}
Buttons for Theme Selection
With the theme manager ready to use, we can pass the methods into onclick
event to set the user theme. You can wrap the SVG icon with the button
element if you plan to use icons. Make sure to add aria-label
attribute so the screen reader can read the button description.
Here is the structure of the buttons. Replace SVG Icon
with the actual SVG value you can copy from free SVG icons sites like Feather Icons.
<aside class="select-theme">
<button
onclick="_manager.resetUserTheme()"
aria-label="Enable System Theme"
>
SVG Icon
</button>
<button
onclick="_manager.setTheme('light')"
aria-label="Enable Light Theme"
>
SVG Icon
</button>
<button
onclick="_manager.setTheme('dark')"
aria-label="Enable Dark Theme"
>
SVG Icon
</button>
</aside>
Following the guide above, you should have functional buttons that allow you to update your site theme. I didn’t cover how to style the buttons because it’s beyond the scope of this guide. I only want to show you how I put the structure together by taking inspiration from other sites.