42 lines (35 loc) · 1.34 KB
 1import { $$, asElement, storage } from "./dom";
 2
 3const STORAGE_KEY = "preferred-theme";
 4
 5type Theme = "light" | "dark";
 6
 7const systemTheme = (): Theme =>
 8  window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
 9
10const applyTheme = (theme: Theme): void => {
11  document.documentElement.dataset.theme = theme;
12};
13
14const syncSwitcher = (theme: Theme): void => {
15  $$("[data-theme-label]").forEach((el) => {
16    el.textContent = theme;
17  });
18};
19
20/** Apply the stored (or system) theme and reflect it on the switcher. */
21export const initTheme = (): void => {
22  const stored = storage?.getItem(STORAGE_KEY);
23  const theme: Theme = stored === "light" || stored === "dark" ? stored : systemTheme();
24  applyTheme(theme);
25  syncSwitcher(theme);
26};
27
28/**
29 * Wire the theme switchers. Bound once for the session: a delegated document
30 * listener keeps every switcher working — the mobile-menu copy lives in the
31 * header, which the router replaces on each swap. Each click toggles light/dark.
32 */
33export const bindThemeControls = (): void => {
34  document.addEventListener("click", (e) => {
35    if (!asElement(e.target)?.closest(".theme-switcher")) return;
36    const theme: Theme =
37      document.documentElement.dataset.theme === "dark" ? "light" : "dark";
38    applyTheme(theme);
39    syncSwitcher(theme);
40    storage?.setItem(STORAGE_KEY, theme);
41  });
42};