Implement theme using SCSS
Instructions for implement dark/light theme and how to switch between them. The theme is persisted when user reload page, also support user preference and sync the setting across multiple tabs.
Basic setup
Create theme CSS variables:
:root,
[data-theme='light'] {
--body-color: #212529;
--body-bg: #fff;
}
[data-theme='dark'] {
--body-color: #adb5bd;
--body-bg: #212529;
}
body {
background-color: var(--body-bg);
color: var(--body-color);
}
Create a button to switch between light and dark theme:
<p>
<button id="theme-switcher">
Switch theme
</button>
</p>
Attach below toggleTheme
function to button's click event:
const toggleTheme = () => {
const theme = document.documentElement.getAttribute('data-theme');
const newTheme = theme === 'dark' ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', newTheme);
}
window.addEventListener('load', () => {
const themeSwitcher = document.getElementById('theme-switcher');
themeSwitcher.addEventListener('click', toggleTheme);
});
Full snippet:
<p>
<button id="theme-switcher">
Switch theme
</button>
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse in malesuada risus. Nunc sit amet ipsum dapibus, dignissim ex non, vestibulum mauris.
</p>
:root,
[data-theme='light'] {
--body-color: #212529;
--body-bg: #fff;
}
[data-theme='dark'] {
--body-color: #adb5bd;
--body-bg: #212529;
}
body {
background-color: var(--body-bg);
color: var(--body-color);
}
const toggleTheme = () => {
const theme = document.documentElement.getAttribute('data-theme');
const newTheme = theme === 'dark' ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', newTheme);
}
window.addEventListener('load', () => {
const themeSwitcher = document.getElementById('theme-switcher');
themeSwitcher.addEventListener('click', toggleTheme);
});
That's it. You already implement the theme feature for your page. It works nicely, but I guess you already notice something is missing. If you reload the page, the them you chose will be reset.
Persist to localStorage
To persist the theme preference, you can store it in localStorage
. First, update the toggleTheme
function as below:
const toggleTheme = () => {
const theme = document.documentElement.getAttribute('data-theme');
const newTheme = theme === 'dark' ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', newTheme);
localStorage.setItem('myTheme1', newTheme);
}
Add new function in the <head>
tag and make sure it get executed as soon as possible when user visits the page. If you put it at the end of <body>
tag, it will be called after the page is rendered, thus will have flickering issue.
const loadTheme = () => {
let theme = localStorage.getItem('myTheme1');
document.documentElement.setAttribute('data-theme', theme)
}
loadTheme();
Full snippet:
const loadTheme = () => {
let theme = localStorage.getItem('myTheme1');
document.documentElement.setAttribute('data-theme', theme)
}
loadTheme();
const toggleTheme = () => {
const theme = document.documentElement.getAttribute('data-theme');
const newTheme = theme === 'dark' ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', newTheme);
localStorage.setItem('myTheme1', newTheme);
}
window.addEventListener('load', () => {
const themeSwitcher = document.getElementById('theme-switcher');
themeSwitcher.addEventListener('click', toggleTheme);
});
Auto select theme based on user's default perference
Many of you use dark mode in browser, up to this point the theme switcher mechanism does not take it into account. To support it, update the loadTheme
function and add conditional checking for (prefers-color-scheme: dark)
media as below:
const loadTheme = () => {
let theme = localStorage.getItem('myTheme2');
if (theme === 'dark'
|| (!theme && window.matchMedia("(prefers-color-scheme: dark)").matches)) {
document.documentElement.setAttribute('data-theme', 'dark');
}
}
loadTheme();
Sync theme preference between tabs
When you save something to localStorage
, browser will sends an event globally across all tabs with same origin. You can take advantage of this feature to sync the theme preference to all tabs when user changes theme by listening to the storage
event as below.
window.addEventListener("storage", (event) => {
switch (event.key) {
case 'myTheme3': {
const theme = event.newValue;
if (theme === 'dark' || theme === 'light') {
document.documentElement.setAttribute('data-theme', theme);
}
}
}
});