GitHub

Astro

Adding dark mode to your Astro app.

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>
</script>

Add a mode toggle

import { createEffect, createSignal } from "solid-js"
 
import { As } from "@kobalte/core"
import { TbDeviceLaptop, TbMoon, TbSun } from "solid-icons/tb"
 
import { Button } from "~/components/ui/button"
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger
} from "~/components/ui/dropdown-menu"
 
export function ModeToggle() {
  const [theme, setTheme] = createSignal<"light" | "dark" | "system">("light")
 
  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")
  })
 
  return (
    <DropdownMenu>
      <DropdownMenuTrigger asChild>
        <As component={Button} variant="ghost" size="sm" class="w-9 px-0">
          <TbSun class="size-6 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
          <TbMoon class="absolute size-6 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
          <span class="sr-only">Toggle theme</span>
        </As>
      </DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuItem onSelect={() => setTheme("light")}>
          <TbSun class="mr-2 size-4" />
          <span>Light</span>
        </DropdownMenuItem>
        <DropdownMenuItem onSelect={() => setTheme("dark")}>
          <TbMoon class="mr-2 size-4" />
          <span>Dark</span>
        </DropdownMenuItem>
        <DropdownMenuItem onSelect={() => setTheme("system")}>
          <TbDeviceLaptop class="mr-2 size-4" />
          <span>System</span>
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  )
}

Display the mode toggle

Place a mode toggle on your site to toggle between light and dark mode.

---
import '~/styles/globals.css'
import { ModeToggle } from '~/components/mode-toggle';
---
 
<!-- Inline script -->
 
<html lang="en">
	<body>
      <h1>Astro</h1>
      <ModeToggle client:only />
	</body>
</html>