export type Theme = "auto" | "light" | "dark"; const COOKIE = "theme"; const YEAR = 60 * 60 * 24 * 365; export const parseTheme = (raw: string | null | undefined): Theme => raw === "light" || raw === "dark" ? raw : "auto"; // the server reads the raw header so the hook works on any request shape export const themeFromCookies = (header: string | null): Theme => { for (const part of header?.split(";") ?? []) { const [name, value] = part.trim().split("="); if (name === COOKIE) return parseTheme(value); } return "auto"; }; // auto leaves the attribute off so the dark variant falls back to prefers-color-scheme export const themeAttribute = (theme: Theme): string => theme === "auto" ? "" : ` data-theme="${theme}"`; export const readTheme = (): Theme => themeFromCookies(document.cookie); export const applyTheme = (theme: Theme): void => { const root = document.documentElement; if (theme === "auto") root.removeAttribute("data-theme"); else root.dataset.theme = theme; document.cookie = theme === "auto" ? `${COOKIE}=; path=/; max-age=0; samesite=lax` : `${COOKIE}=${theme}; path=/; max-age=${YEAR}; samesite=lax`; };