Dark Mode
Adding dark mode to your Astro project.
Adding dark mode to your Astro project.
Create an inline theme script
Section titled “Create an inline theme script”---import "../styles/globals.css";---
<script is:inline> const getThemePreference = () => { if (typeof localStorage !== "undefined" && localStorage.getItem("theme")) { return localStorage.getItem("theme"); } return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; }; const isDark = getThemePreference() === "dark"; document.documentElement.classList[isDark ? "add" : "remove"]("dark");
if (typeof localStorage !== "undefined") { const observer = new MutationObserver(() => { const isDark = document.documentElement.classList.contains("dark"); localStorage.setItem("theme", isDark ? "dark" : "light"); }); observer.observe(document.documentElement, { attributes: true, attributeFilter: ["class"], }); }</script>
<html lang="en"> <body> <h1>Astro</h1> </body></html>Add a mode toggle
Section titled “Add a mode toggle”---import { Select, SelectControl, SelectIndicator, SelectOption } from "@bejamas/ui/components/select";import { SunIcon, MoonIcon, LaptopIcon } from "@lucide/astro";---
<div class="relative text-sm"> <div class="absolute z-10 left-3 top-2.5"> <SunIcon class="size-4 dark:hidden" /> <MoonIcon class="size-4 hidden dark:block" /> </div> <Select showCheckmark={false}> <SelectIndicator /> <SelectControl onchange="window.toggleTheme(this.value)" id="theme-select" showCheckmark={false} class="pl-9 w-28" > <SelectOption value="light" ><SunIcon class="size-4" /> Light</SelectOption > <SelectOption value="dark" ><MoonIcon class="size-4" /> Dark</SelectOption > <SelectOption value="auto" ><LaptopIcon class="size-4" /> Auto</SelectOption > </SelectControl> </Select></div>
<script is:inline> // @ts-nocheck const storageKey = "starlight-theme"; const themeMetaMedias = [ "(prefers-color-scheme: light)", "(prefers-color-scheme: dark)", ]; const themeColors = { light: "#ffffff", dark: "#0f171f", };
const parseTheme = (theme) => theme === "auto" || theme === "dark" || theme === "light" ? theme : "auto";
const loadTheme = () => { try { if (typeof localStorage !== "undefined") { return parseTheme(localStorage.getItem(storageKey)); } } catch {} return "auto"; };
function storeTheme(theme) { try { if (typeof localStorage !== "undefined") { localStorage.setItem( storageKey, theme === "light" || theme === "dark" ? theme : "", ); } } catch {} }
function ensureThemeMeta(head, media) { const baseSelector = `meta[name="theme-color"][media="${media}"]`;
let meta = head.querySelector(`${baseSelector}[data-theme-managed]`); if (meta instanceof HTMLMetaElement) return meta;
meta = head.querySelector(baseSelector); if (meta instanceof HTMLMetaElement) { meta.setAttribute("data-theme-managed", ""); return meta; }
meta = document.createElement("meta"); meta.setAttribute("name", "theme-color"); meta.setAttribute("media", media); meta.setAttribute("data-theme-managed", ""); head.appendChild(meta); return meta; }
function syncThemeColor(theme) { const color = themeColors[theme]; const head = document.head; if (!color || !head) return;
themeMetaMedias.forEach((media) => { const meta = ensureThemeMeta(head, media); meta.setAttribute("content", color); }); }
const getPreferredColorScheme = () => matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark";
function applyTheme(theme) { const effective = theme === "auto" ? getPreferredColorScheme() : theme; document.documentElement.dataset.theme = effective; document.documentElement.classList.remove("light", "dark"); document.documentElement.classList.add(effective); syncThemeColor(effective); storeTheme(theme); const select = document.getElementById("theme-select"); if (select instanceof HTMLSelectElement) select.value = theme; }
// Initialize applyTheme(loadTheme());
// UI: select changes const select = document.getElementById("theme-select"); if (select instanceof HTMLSelectElement) { select.addEventListener("change", (e) => { if (e.currentTarget instanceof HTMLSelectElement) { applyTheme(parseTheme(e.currentTarget.value)); } }); }
window.toggleTheme = (theme) => applyTheme(parseTheme(theme));
// React to system scheme changes while in 'auto' matchMedia("(prefers-color-scheme: light)").addEventListener("change", () => { if (loadTheme() === "auto") applyTheme("auto"); });</script>Display the mode toggle
Section titled “Display the mode toggle” Theme Editor
⌘E
Place a mode toggle on your site to toggle between light and dark mode.
---import "../styles/globals.css";import ThemeSwitcher from "@/components/ThemeSwitcher.astro";---
<!-- Inline script --><html lang="en"> <body> <h1>Astro</h1> <ThemeSwitcher client:load /> </body></html>