fshr
The musings of a grumpy hairless ape
Web Theme Selection
After posting about doing auto image resizing on this site, the next thing I’d been looking to tackle was properly implementing dark/light theme support. I’d previously done something basic CSS selectors to load specific CSS based on the browser/OS preference, but there wasn’t any way for a reader to manually override the option if they wanted something different to the browser/OS option.
The below solution is based on this post with some fixes and modifications. Specifically the example above didn’t correctly restore the theme if the user had manually selected ’light’, and I’ve added an indication showing if it’s an Automatic (browser/OS) theme selection.
I’m aware that there’s probably a whole set of different ways to do this, and I could probably make this “better” in various ways. Also, while I would prefer to be able to do this without any JavaScript, it is required for the interactive switching as well as storing/loading the user’s preference to/from local storage (to be able to persist the choice). In the event that a reader has JavaScript disabled, the page will default to the “light” theme.
What is required?
There needs to be three changes made to support theme switching. Note that this is how I’ve done it for my site, but you may/will want to change and tweak as required!
CSS
First thing is to set up the CSS appropriately. Here, I’ve used a combination of CSS selectors and HTML body classes to set a bunch of CSS variables which are then used later on in my CSS for various formatting.
The :root selector defines the default “light” colour scheme, and the body.dark class defines the “dark” colour scheme. By default, the :root selector will get applied by the browser (so giving the light theme), unless we specifically add the body.dark class to the HTML body element (using the JavaScript) to override those variables with the dark variants.
:root {
--general-background-colour: White;
--general-text-colour: Black;
}
body.dark {
--general-background-colour: Black;
--general-text-colour: White;
}
body {
color: var(--general-text-colour);
background: var(--general-background-colour);
}
Note the above is just an example CSS, you can define as many variables as you want, and use them wherever you want within your HTML (see my CSS for example of this site).
You’ll also need some CSS to “prettify” the selector toggle. This is largely taken as-is from the article linked above, other than the last 3 classes to add the indication of whether this is an automatic selection (i.e. browser/OS set and not manually set).
.switch {
position: relative;
display: inline-block;
width: 44px;
height: 24px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.switch input:checked + .slider {
background-color: #476173;
}
.switch input:checked + .slider:before {
transform: translateX(20px);
background-color: transparent;
border-radius: 50%;
border-top-color: transparent;
border-left-color: transparent;
border-right-color: transparent;
box-shadow: inset -5px -3px 0 #d8e9ef;
}
.switch .slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #7bb9f5;
transition: 0.4s;
border-radius: 20px;
box-shadow: 0 0 0.25em rgba(67, 71, 85, 0.27), 0.2px 0.2em 24px 0 rgba(1, 29, 77, 0.15);
}
.switch .slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 4px;
bottom: 4px;
background-color: yellow;
transition: 0.4s;
border-radius: 50%;
text-align: center;
line-height: 16px;
}
/* My additions below */
.switch .switch-auto:before {
content: "A";
}
.switch .switch-auto {
color: black;
text-shadow: 0px 0px 5px white;
font-size: 10px;
}
.switch input:checked + .switch-auto {
color: whitesmoke;
text-shadow: 0px 0px 5px black;
}
JavaScript
Second thing to include on the site is the JavaScript. This will need to be available for all pages, as it handles both the user switching between dark/light as well as setting the appropriate theme on page load. Simplest is (probably) to save in a separate .js file and load with a <script> tag within the page <head>.
document.addEventListener('DOMContentLoaded', function() {
const themeSwitcher = document.getElementById('theme-switcher');
const themeSwitcherSlider = document.getElementById('theme-switcher-slider');
// Load saved theme preference
const savedTheme = localStorage.getItem('theme');
// If saved option is 'dark'
if (savedTheme === 'dark') {
document.body.classList.add('dark');
themeSwitcher.checked = true;
}
// Else if saved option is 'light'
else if (savedTheme === 'light') {
themeSwitcher.checked = false;
}
// Else if no saved preference, then check if the OS preference is 'dark'
else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
document.body.classList.add('dark');
themeSwitcherSlider.classList.add('switch-auto');
themeSwitcher.checked = true;
}
// Else saved option is 'light'
else {
themeSwitcherSlider.classList.add('switch-auto');
themeSwitcher.checked = false;
}
// Handle toggle switch
themeSwitcher.addEventListener('change', function() {
document.body.classList.toggle('dark');
themeSwitcherSlider.classList.remove('switch-auto');
localStorage.setItem('theme', this.checked ? 'dark' : 'light');
});
});
If all the above is in place correctly, then on loading the page:
- It will check for the presence of a saved choice in local browser storage
- If it finds “dark” it will apply the appropriate body class and sets the slider position
- If it finds “light” it leaves the styling as is (default light) and sets the slider position
- If no saved option is set it will look to see what the browser setting is
- If it finds “dark” it will apply the appropriate body class, set the slider position, and add the “auto” class
- If it finds “light” it leaves the styling as is, sets the slider position, and adds the “auto” class
- It then listens for a change to the toggle setting, and on a change
- Switches the body class on/off as required
- Removes the “auto” class (as the reader’s now set a preference)
- Writes the choice to the local browser storage
HTML
Finally we add the HTML to present the theme selector. This is styled using the above CSS to present a nice looking toggle slider.
<label class="switch">
<input type="checkbox" id="theme-switcher">
<span class="slider round" id="theme-switcher-slider"></span>
</label>
Job done!
Content borrowed from
https://zerowp.com/how-to-implement-a-light-dark-theme-switcher-with-css-variables/