From 60ba1a4fb5debe32521c36763154b8e41984f504 Mon Sep 17 00:00:00 2001 From: mkelvers Date: Fri, 5 Jun 2026 16:23:59 +0200 Subject: [PATCH] refactor: follow system color scheme via matchMedia listener --- static/theme.ts | 50 ++++--------------------------------------------- 1 file changed, 4 insertions(+), 46 deletions(-) diff --git a/static/theme.ts b/static/theme.ts index e2afea6..104937c 100644 --- a/static/theme.ts +++ b/static/theme.ts @@ -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") {