GitHub

Astro

How to enable dark mode for Astro app

Create An Inline Theme Script#

src/pages/index.astro
import '../styles/globals.css'
<html lang="en">
<body>
<h1>Astro</h1>
</body>
</html>
<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>

Add A Mode Toggler#

Toggle#

src/components/mode-toggle.tsx
import { createEffect, createSignal, onMount } from "solid-js";
import Moon from "lucide-solid/icons/moon";
import Sun from "lucide-solid/icons/sun";
import { Toggle } from "~/components/ui/toggle";
export default function ModeToggle() {
const [theme, setTheme] = createSignal<"light" | "dark" | "system">("light");
onMount(() => {
const isDarkMode = document.documentElement.classList.contains("dark");
setTheme(isDarkMode ? "dark" : "light");
});
createEffect(() => {
const isDark =
theme() === "dark" ||
(theme() === "system" &&
window.matchMedia("(prefers-color-scheme: dark)").matches);
document.documentElement.classList[isDark ? "add" : "remove"]("dark");
document.documentElement.dataset.theme = isDark
? "catppuccin-macchiato"
: "catppuccin-latte";
``;
});
const toggleMode = () => {
if (theme() === "light") {
setTheme("dark");
} else if (theme() === "dark") {
setTheme("light");
}
};
return (
<Toggle onClick={toggleMode} variant="transparent">
<Moon class="size-5 scale-0 -rotate-90 transition-transform dark:scale-100 dark:rotate-0" />
<Sun class="absolute size-5 scale-100 rotate-0 transition-transform dark:scale-0 dark:-rotate-90" />
</Toggle>
);
}
src/components/mode-toggle.tsx
import Moon from "lucide-solid/icons/moon";
import Sun from "lucide-solid/icons/sun";
import { setMode } from "solid-mode-watcher";
import { Button } from "./ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "./ui/dropdown-menu";
export default function ModeToggle() {
return (
<DropdownMenu>
<DropdownMenuTrigger as={Button} size="icon" variant="neutral-no-shadow">
<Moon class="size-5 scale-0 -rotate-90 transition-transform dark:scale-100 dark:rotate-0" />
<Sun class="absolute size-5 scale-100 rotate-0 transition-transform dark:scale-0 dark:-rotate-90" />
<span class="sr-only">Toggle theme</span>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem onClick={() => setMode("light")}>
Light
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setMode("dark")}>
Dark
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setMode("system")}>
System
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}

Display The Mode Toggler#

src/layouts/layout.astro
---
import { ModeToggle } from "~/components/mode-toggle"; import "~/styles/global.css";
---
<html lang="en">
<body>
<slot />
<ModeToggle client:load />
</body>
</html>