refactor: follow system color scheme via matchMedia listener

This commit is contained in:
2026-06-05 16:23:59 +02:00
parent 3ea5ea68ff
commit 60ba1a4fb5

View File

@@ -1,62 +1,20 @@
type Theme = "light" | "dark";
const STORAGE_KEY = "theme";
const getLocalStorage = (): Storage | null => {
try {
return window.localStorage;
} catch {
return null;
}
};
const colorSchemeQuery = window.matchMedia?.("(prefers-color-scheme: dark)") ?? null;
const getPreferredTheme = (): Theme => {
const prefersDark = window.matchMedia?.("(prefers-color-scheme: dark)")?.matches ?? false;
const prefersDark = colorSchemeQuery?.matches ?? false;
return prefersDark ? "dark" : "light";
};
const normalizeTheme = (raw: string | null): Theme | null => {
if (raw === "light" || raw === "dark") return raw;
return null;
};
const getSavedTheme = (): Theme => {
const storage = getLocalStorage();
const fromStorage = normalizeTheme(storage?.getItem(STORAGE_KEY) ?? null);
if (fromStorage) return fromStorage;
return getPreferredTheme();
};
const applyTheme = (theme: Theme): void => {
document.documentElement.setAttribute("data-theme", theme);
document.documentElement.style.colorScheme = theme;
const storage = getLocalStorage();
try {
storage?.setItem(STORAGE_KEY, theme);
} catch {
// ignore
}
};
const cycleTheme = (): void => {
const current = getSavedTheme();
const next: Theme = current === "light" ? "dark" : "light";
applyTheme(next);
};
const initTheme = (): void => {
const saved = getSavedTheme();
applyTheme(saved);
// delegated click handler on theme buttons
document.addEventListener("click", (e) => {
const target = e.target;
if (!(target instanceof Element)) return;
const btn = target.closest("#theme-toggle, #footer-theme-toggle");
if (!(btn instanceof HTMLButtonElement)) return;
cycleTheme();
});
applyTheme(getPreferredTheme());
colorSchemeQuery?.addEventListener("change", () => applyTheme(getPreferredTheme()));
};
if (document.readyState === "loading") {