Files

33 lines
1.5 KiB
TypeScript
Raw Permalink Normal View History

2026-09-23 21:57:24 +02:00
/* ============================================================================
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`;
}