Skip to content
Theme Editor
⌘E
Background
L 0.5
C 0.2
H 250
Foreground
L 0.5
C 0.2
H 250
Primary
L 0.5
C 0.2
H 250
Primary FG
L 0.5
C 0.2
H 250
Secondary
L 0.5
C 0.2
H 250
Secondary FG
L 0.5
C 0.2
H 250
Accent
L 0.5
C 0.2
H 250
Accent FG
L 0.5
C 0.2
H 250
Card
L 0.5
C 0.2
H 250
Card FG
L 0.5
C 0.2
H 250
Popover
L 0.5
C 0.2
H 250
Popover FG
L 0.5
C 0.2
H 250
Muted
L 0.5
C 0.2
H 250
Muted FG
L 0.5
C 0.2
H 250
Border
L 0.5
C 0.2
H 250
Input
L 0.5
C 0.2
H 250
Ring
L 0.5
C 0.2
H 250
Destructive
L 0.5
C 0.2
H 250
Destructive FG
L 0.5
C 0.2
H 250
Chart 1
L 0.5
C 0.2
H 250
Chart 2
L 0.5
C 0.2
H 250
Chart 3
L 0.5
C 0.2
H 250
Chart 4
L 0.5
C 0.2
H 250
Chart 5
L 0.5
C 0.2
H 250

Dark Mode

Adding dark mode to your Astro project.

Adding dark mode to your Astro project.

src/layouts/index.astro
---
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>
src/components/ThemeSwitcher.astro
---
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>
Theme Editor
⌘E
Background
L 0.5
C 0.2
H 250
Foreground
L 0.5
C 0.2
H 250
Primary
L 0.5
C 0.2
H 250
Primary FG
L 0.5
C 0.2
H 250
Secondary
L 0.5
C 0.2
H 250
Secondary FG
L 0.5
C 0.2
H 250
Accent
L 0.5
C 0.2
H 250
Accent FG
L 0.5
C 0.2
H 250
Card
L 0.5
C 0.2
H 250
Card FG
L 0.5
C 0.2
H 250
Popover
L 0.5
C 0.2
H 250
Popover FG
L 0.5
C 0.2
H 250
Muted
L 0.5
C 0.2
H 250
Muted FG
L 0.5
C 0.2
H 250
Border
L 0.5
C 0.2
H 250
Input
L 0.5
C 0.2
H 250
Ring
L 0.5
C 0.2
H 250
Destructive
L 0.5
C 0.2
H 250
Destructive FG
L 0.5
C 0.2
H 250
Chart 1
L 0.5
C 0.2
H 250
Chart 2
L 0.5
C 0.2
H 250
Chart 3
L 0.5
C 0.2
H 250
Chart 4
L 0.5
C 0.2
H 250
Chart 5
L 0.5
C 0.2
H 250

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

src/pages/index.astro
---
import "../styles/globals.css";
import ThemeSwitcher from "@/components/ThemeSwitcher.astro";
---
<!-- Inline script -->
<html lang="en">
<body>
<h1>Astro</h1>
<ThemeSwitcher client:load />
</body>
</html>