20
Create a Simple Dark Mode Toggle With Svelte
Note: While this tutorial is focused on Svelte, it should be possible to make it work with other UI frameworks as well. Feel free to give it a try!
You might have noticed that light/dark mode toggles are everywhere right now - pretty much every popular website allows users to choose which theme they prefer. So how do you add a theme toggle to your Svelte app?
Luckily, its pretty easy - here's what you need to do:
Create a new component, DarkModeToggle.svelte
, and add a new checkbox inside it:
<input type="checkbox" on:click={toggleTheme} />
Checkboxes are pretty good choices for toggles like this one because they are widely supported and represent Boolean states. In our case, false
represents "light off" (= "dark"), while true
represents "on".
Append the new component to your app.
Next, we need to implement the toggleTheme
handler. Add a script
tag to your component:
<script>
const STORAGE_KEY = 'theme';
const DARK_PREFERENCE = '(prefers-color-scheme: dark)';
const THEMES = {
DARK: 'dark',
LIGHT: 'light',
};
const prefersDarkThemes = () => window.matchMedia(DARK_PREFERENCE).matches;
const toggleTheme = () => {
const stored = localStorage.getItem(STORAGE_KEY);
if (stored) {
// clear storage
localStorage.removeItem(STORAGE_KEY);
} else {
// store opposite of preference
localStorage.setItem(STORAGE_KEY, prefersDarkThemes() ? THEMES.LIGHT : THEMES.DARK);
}
// TODO: apply new theme
};
</script>
...
As you can see, there's quite a lot going on in here:
- We use
localStorage
to store a user's theme preference. - We use a media query to figure out whether the user has a theme preference set in their OS.
- If the user generally prefers dark themes and also picks the dark theme on our website, we do not need to store a preference. The same is true for users who do not use dark themes both generally and on our site.
For all others, we store their preference in their
localStorage
.
Finally, we need to apply the new theme. Add another function to your file and call it at the end of toggleTheme
:
const applyTheme = () => {
const preferredTheme = prefersDarkThemes() ? THEMES.DARK : THEMES.LIGHT;
currentTheme = localStorage.getItem(STORAGE_KEY) ?? preferredTheme;
currentTheme = localStorage.getItem(STORAGE_KEY) ?? preferredTheme;
if (currentTheme === THEMES.DARK) {
document.body.classList.remove(THEMES.LIGHT);
document.body.classList.add(THEMES.DARK);
} else {
document.body.classList.remove(THEMES.DARK);
document.body.classList.add(THEMES.LIGHT);
}
};
const toggleTheme = () => {
...
applyTheme();
};
Looks good so far! However, we still don't handle some edge cases:
- How do we apply the initial theme when a user first visits our website?
- What happens when a user changes their system wide theme preference?
We can take care of both situations in onMount
:
import { onMount } from 'svelte';
...
onMount(() => {
applyTheme();
window.matchMedia(DARK_PREFERENCE).addEventListener('change', applyTheme);
});
Last but not least, adapt the checkbox so it's state matches the theme:
<input type="checkbox" checked={currentTheme !== THEMES.DARK} on:click={toggleTheme} />
Now, all that's left to do is:
We can now write custom styles for dark mode:
@media (prefers-color-scheme: dark) {
body:not(.light) {
/* Your stuff here... */
}
}
body.dark {
/* And also here... */
}
Aaaaand we're done! 🎉
If you are interested in a fully working example, check out my webapp Manicure (source code). It might also give you some ideas how to style the toggle.
20