Introduction
Enhancing accessibility on your website is made easier by introducing a dark mode. In this guide, we’ll explore the steps to seamlessly integrate a polished dark mode into your Astro project. This guide focuses on using Astro 3.0 and above and having view transitions integrated.
Why does View Transitions break dark modes?
With ViewTransition, it’s important to note that certain code is executed only once during the first page visit and not upon navigating to subsequent pages. This behavior can impact dark mode implementations, especially if the code responsible for managing dark mode is expected to run with each page transition. To ensure consistent dark mode behavior across pages, developers need to carefully manage the execution of relevant code within the Astro View Transition API lifecycle events
astro:after-swap
The astro:after-swap event is a pivotal point in the navigation lifecycle, firing immediately after the new page replaces the old page. By listening to this event on the document, developers can execute actions before the new page’s DOM elements render and scripts run
The code
The code below should be marked as is:inline to make it not blocking and avoid a flash when initally loading the site. The code should be placed inside the <head></head>
in your layout.astro.
<script is:inline>
const setTheme = () => {
const theme = (() => {
if (
typeof localStorage !== "undefined" &&
localStorage.getItem("theme")
) {
return localStorage.getItem("theme");
}
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
return "dark";
}
return "light";
})();
if (theme === "light") {
document.documentElement.classList.remove("dark");
} else {
document.documentElement.classList.add("dark");
}
window.localStorage.setItem("theme", theme || "light");
};
setTheme();
document.addEventListener("astro:after-swap", setTheme);
</script>
We begin by defining a function called setTheme
, which dynamically determines the theme to be applied. The theme is first checked in the local storage, and if found, it is retrieved; otherwise, it checks the system’s preferred color scheme using prefers-color-scheme
. If neither is available, it defaults to a light theme. The code then applies the chosen theme to the document’s root element, either adding or removing the class “dark” accordingly. The final step involves storing the chosen theme in the local storage
for future use. Additionally, an event listener is set up to trigger the setTheme function after “astro:after-swap” event. This ensures the consistent application of the chosen theme across page changes.
Adding a toggle
<button id="themeToggle">
<span class="sr-only">Dark theme</span>
<span class="icon light"><Icon icon="sun" /></span>
<span class="icon dark"><Icon icon="moon-stars" /></span>
</button>
<script>
const setToggleListener = () => {
let toggleBtn = document.getElementById("themeToggle");
const handleToggleClick = () => {
const element = document.documentElement;
element.classList.toggle("dark");
const isDark = element.classList.contains("dark");
localStorage.setItem("theme", isDark ? "dark" : "light");
};
toggleBtn?.addEventListener("click", handleToggleClick);
};
setToggleListener();
document.addEventListener("astro:after-swap", setToggleListener);
</script>
The code defines a function called setToggleListener
, which initializes the event listener for the theme toggle button. Inside this function, the button element is obtained using document.getElementById(“themeToggle”). The handleToggleClick
function is then defined to toggle the “dark” class on the document’s root element (document.documentElement) when the button is clicked. Additionally, it checks whether the “dark” class is present to determine if the current theme is dark, and it updates the local storage with the corresponding theme (“dark” or “light”).
The addEventListener method is used to listen for the “click” event on the theme toggle button, triggering the handleToggleClick
function when the button is clicked.
To ensure that the theme toggle functionality persists across page transitions in an Astro framework application, an event listener is set up for the “astro:after-swap” event. This event triggers the setToggleListener
function, ensuring that the theme toggle functionality is consistently applied even after navigating between pages.