How To: Toggle Dark Mode
Last updatedYou 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, it’s pretty easy - here’s what you need to do:
Step 1: Add a Toggle Component
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.
Step 2: Toggle Themes
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
.
Step 3: Apply the Theme
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:
Step 4: Write Custom CSS
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! 🎉