1 days ago · 2 min read · Web Development
How To Do Dark Mode With Static Pages
When you don't have server rendered pages that let you send down a page ready for the user's selected color scheme, it might seem hard. Believe me, it's not!
This website has a light and dark mode, and responds to the user’s preferred color scheme out of the box.
Getting this to work with my fully static website proved to be a real chore, and ended in a ridiculously simple solution.
So for this tutorial, I’ll walk you through setting up a simple light and dark theme, with a button to toggle in on and off.
What we need to do
We need to do a few things:
We need to get the user’s operating system (OS) preferences, if any
We need to apply the users preference to our site
We need a button to allow the user to toggle between light and dark, overriding their OS preference
So, let’s dive right in…
Getting the OS preference
To get the OS preference, we’ll use the window.matchMedia
API:
<script>
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
</script>
Now, let’s move on to our button…
Applying the OS preference
Now that we have the users preference, we can use it to apply our theme to the page. We’ll do
that by adding a dark
class if the theme is dark, and having our light theme be the default.
<script>
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches);
const root = document.documentElement;
if (prefersDark) {
root.classList.add("dark");
} else {
root.classList.remove("dark");
}
</script>
Now, we need to add styles for our light and dark themes. For now, we’ll do something very simple:
If the theme is light, the background should be white
If the theme is dark, the background should be black
<style>
html {
background: white;
}
html.dark {
background: black;
}
</style>
This gives us the basic functionality of matching the users operating system preferences. But, what if they don’t want that!?!?
Toggling between light and dark
We’ll create a very simple (and ugly) button to do the toggle.
<button>Toggle theme</button>
Right now, our button doesn’t do anything. Let’s fix that by adding an onclick
event:
<script>
const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches);
const root = document.documentElement;
if (prefersDark) {
root.classList.add("dark");
} else {
root.classList.remove("dark");
}
function toggleTheme() {
const root = document.documentElement;
const isDark = root.classList && root.classList.contains("dark");
if (isDark) {
root.classList.remove("dark");
} else {
root.classList.add("dark");
}
}
</script>
<button onclick='toggleTheme()'>test</button>
Finally, we’ll use localStorage
so we can persist the override between page views, and use the override if it’s present.
<script>
const override = window.localStorage.getItem("theme");
const prefersDark =
override === "dark" ||
(!override &&
window.matchMedia("(prefers-color-scheme: dark)").matches);
const root = document.documentElement;
if (prefersDark) {
root.classList.add("dark");
} else {
root.classList.remove("dark");
}
function toggleTheme() {
const root = document.documentElement;
const isDark = root.classList && root.classList.contains("dark");
if (isDark) {
root.classList.remove("dark");
window.localStorage.setItem("theme", "light");
} else {
root.classList.add("dark");
window.localStorage.setItem("theme", "dark");
}
}
</script>
Finally, we must put the script
tag in the head
element of the page. This ensures that it’s loaded and
executed before any of the styles load. This prevents a flash of light colors for users that are using dark mode.
Putting it all together, we get something that looks like:
<html lang="en">
<head>
<style>
html {
background: white;
}
html.dark {
background: black;
}
</style>
<script>
const override = window.localStorage.getItem("theme");
const prefersDark =
override === "dark" ||
(!override &&
window.matchMedia("(prefers-color-scheme: dark)").matches);
const root = document.documentElement;
if (prefersDark) {
root.classList.add("dark");
} else {
root.classList.remove("dark");
}
function toggleTheme() {
const root = document.documentElement;
const isDark = root.classList && root.classList.contains("dark");
if (isDark) {
root.classList.remove("dark");
window.localStorage.setItem("theme", "light");
} else {
root.classList.add("dark");
window.localStorage.setItem("theme", "dark");
}
}
</script>
</head>
<body>
<button onclick="toggleTheme()">Toggle theme</button>
</body>
</html>
Wrapping up
This gives you a basic example of an API that will work for statically rendered pages.
It’s not perfect, but it gets the job done.