mode.ts
1.6 kB · typescript · 60 lines
1import { MODE_KEY, THEME_COLORS } from "../config/shop.ts";23type Mode = "dark" | "light";45const listeners = new Set<() => void>();67let mode: Mode = "light";89const clean = (value: string | null): Mode | null => (value === "dark" || value === "light" ? value : null);1011const stored = () => {12 try {13 return clean(localStorage.getItem(MODE_KEY));14 } catch {15 return null;16 }17};1819const system = (): Mode => (matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light");2021function paint(next: Mode) {22 mode = next;23 const root = document.documentElement;24 root.dataset.mode = next;25 const meta = document.querySelector<HTMLMetaElement>('meta[name="theme-color"]');26 if (meta) meta.content = THEME_COLORS[next];27 for (const fn of listeners) fn();28 dispatchEvent(new CustomEvent("mode", { detail: next }));29}3031export const get = () => mode;3233export const server = (): Mode => "light";3435export const none = () => null;3637export function subscribe(fn: () => void) {38 listeners.add(fn);39 return () => void listeners.delete(fn);40}4142function set(next: Mode) {43 try {44 localStorage.setItem(MODE_KEY, next);45 } catch {}46 paint(next);47}4849export const toggle = () => set(mode === "dark" ? "light" : "dark");5051export function start() {52 const dark = matchMedia("(prefers-color-scheme: dark)");53 paint(clean(document.documentElement.dataset.mode ?? null) ?? stored() ?? system());54 dark.addEventListener("change", () => {55 if (!stored()) paint(system());56 });57 addEventListener("storage", (event) => {58 if (event.key === MODE_KEY) paint(stored() ?? system());59 });60}