/* ============================================================================ SITE THEME ---------------------------------------------------------------------------- The one global theme decision the site makes. The whole tier-2 palette is still per-surface (see app/styles/semantic.css); this simply lets a reader flip WHICH surface the document stands on, `:root` dark by default. The preference travels as the `sharper-theme` cookie so the server renders the same surface the next page will paint - no flash-of-wrong-theme, no hydration mismatch. ========================================================================= */ export type AppTheme = "dark" | "light"; export const THEME_COOKIE = "sharper-theme"; /** Default when no cookie is present. `:root` is the dark surface. */ export const DEFAULT_THEME: AppTheme = "dark"; /** Parse the cookie header into a theme. Anything unknown resolves to dark. */ export function readTheme(cookieHeader: string | null): AppTheme { if (!cookieHeader) return DEFAULT_THEME; const pair = cookieHeader .split(";") .map((part) => part.trim()) .find((part) => part.startsWith(`${THEME_COOKIE}=`)); const value = pair?.split("=")[1]; return value === "light" ? "light" : value === "dark" ? "dark" : DEFAULT_THEME; } /** The Set-Cookie value that persists a theme choice for a year. */ export function themeCookie(theme: AppTheme): string { return `${THEME_COOKIE}=${theme}; Path=/; Max-Age=31536000; SameSite=Lax`; }