55 lines
1.8 KiB
JavaScript
55 lines
1.8 KiB
JavaScript
(() => {
|
|
const storageKey = "gl-theme";
|
|
const root = document.documentElement;
|
|
|
|
const storedTheme = () => {
|
|
try {
|
|
const value = localStorage.getItem(storageKey);
|
|
return value === "dark" || value === "light" ? value : "";
|
|
} catch (_) {
|
|
return "";
|
|
}
|
|
};
|
|
|
|
const systemTheme = () =>
|
|
window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
|
|
const updateControls = (theme) => {
|
|
document.querySelectorAll("[data-theme-toggle]").forEach((control) => {
|
|
const dark = theme === "dark";
|
|
control.setAttribute("aria-pressed", String(dark));
|
|
control.setAttribute("aria-label", dark ? control.dataset.lightLabel : control.dataset.darkLabel);
|
|
const icon = control.querySelector("[data-theme-icon]");
|
|
if (icon) icon.textContent = dark ? "☀" : "☾";
|
|
});
|
|
};
|
|
|
|
const applyTheme = (theme) => {
|
|
root.dataset.theme = theme;
|
|
root.style.colorScheme = theme;
|
|
updateControls(theme);
|
|
};
|
|
|
|
applyTheme(storedTheme() || systemTheme());
|
|
|
|
document.addEventListener("DOMContentLoaded", () => updateControls(root.dataset.theme));
|
|
document.addEventListener("htmx:afterSwap", () => updateControls(root.dataset.theme));
|
|
document.addEventListener("click", (event) => {
|
|
if (!(event.target instanceof Element)) return;
|
|
const control = event.target.closest("[data-theme-toggle]");
|
|
if (!control) return;
|
|
const theme = root.dataset.theme === "dark" ? "light" : "dark";
|
|
try {
|
|
localStorage.setItem(storageKey, theme);
|
|
} catch (_) {
|
|
// The selected theme still applies for this page when storage is unavailable.
|
|
}
|
|
applyTheme(theme);
|
|
});
|
|
|
|
const media = window.matchMedia("(prefers-color-scheme: dark)");
|
|
media.addEventListener?.("change", () => {
|
|
if (!storedTheme()) applyTheme(systemTheme());
|
|
});
|
|
})();
|