import { browser } from "$app/environment"; /** Canonical preference key (app-wide: marketing, dashboard, admin, auth). */ export const THEME_KEY = "descrybe-theme"; /** Pre-unification marketing key — read once, then migrate to THEME_KEY. */ export const LEGACY_MARKETING_THEME_KEY = "descrybe-marketing-theme"; /** Brief dual-store key from ui-theme experiment — migrate to THEME_KEY. */ export const LEGACY_UI_THEME_KEY = "descrybe-ui-theme"; export type Theme = "light" | "dark"; /** SSR / first paint / unset storage — always light (no OS prefers-color-scheme). */ export const THEME_DEFAULT: Theme = "light"; /** Match layout.css --background (FOUC paint before CSS loads). */ const DARK_PAINT = "hsl(252 22% 9%)"; const LIGHT_PAINT = "hsl(240 20% 98%)"; function normalize(value: string | null | undefined): Theme | null { if (value === "light" || value === "dark") return value; return null; } function readStored(): Theme { if (!browser) return THEME_DEFAULT; try { const fromWindow = normalize(window.__THEME__) ?? normalize(window.__MARKETING_THEME__) ?? normalize(window.__UI_THEME__); if (fromWindow) return fromWindow; const fromStorage = normalize(localStorage.getItem(THEME_KEY)) ?? normalize(localStorage.getItem(LEGACY_MARKETING_THEME_KEY)) ?? normalize(localStorage.getItem(LEGACY_UI_THEME_KEY)); if (fromStorage) return fromStorage; } catch { /* ignore */ } return THEME_DEFAULT; } function paintDocument(next: Theme): void { if (!browser) return; const root = document.documentElement; root.classList.toggle("dark", next === "dark"); root.dataset.theme = next; root.style.colorScheme = next; root.style.backgroundColor = next === "dark" ? DARK_PAINT : LIGHT_PAINT; document.body?.classList.remove("dark"); for (const id of ["marketing-shell", "auth-shell", "admin-shell", "app-shell"] as const) { const shell = document.getElementById(id); if (shell) { shell.classList.toggle("dark", next === "dark"); } } } function createTheme() { let theme = $state(readStored()); function setTheme(next: Theme) { theme = next; if (!browser) return; try { localStorage.setItem(THEME_KEY, next); localStorage.removeItem(LEGACY_MARKETING_THEME_KEY); localStorage.removeItem(LEGACY_UI_THEME_KEY); } catch { /* ignore */ } window.__THEME__ = next; window.__MARKETING_THEME__ = next; window.__UI_THEME__ = next; paintDocument(next); } function toggle() { setTheme(theme === "dark" ? "light" : "dark"); } function syncFromStorage() { const next = readStored(); theme = next; paintDocument(next); } return { get theme() { return theme; }, get isDark() { return theme === "dark"; }, setTheme, toggle, syncFromStorage, paintDocument }; } export const theme = createTheme(); declare global { interface Window { __THEME__?: Theme; /** @deprecated Prefer __THEME__; kept for FOUC / docs lockstep. */ __MARKETING_THEME__?: Theme; /** @deprecated Prefer __THEME__; kept for ui-theme migration. */ __UI_THEME__?: Theme; } }