36
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!
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:
localStorage
to store a user's theme preference.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:
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.
36