From 7bf2113e6aed3eac2a62d6e0ed8bb235b3a92d19 Mon Sep 17 00:00:00 2001 From: stroblme Date: Fri, 21 Aug 2026 15:13:18 +0200 Subject: [PATCH] Let the theme provider render without a browser The sibling index frontend now prerenders its public pages, where the initial state read of localStorage and the matchMedia probe both run with no browser present. The design contract keeps this file byte-identical across the two repos, so the guards land here in the same change. --- frontend/src/components/theme-provider.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/theme-provider.tsx b/frontend/src/components/theme-provider.tsx index a582b28..9d9d9d4 100644 --- a/frontend/src/components/theme-provider.tsx +++ b/frontend/src/components/theme-provider.tsx @@ -35,11 +35,17 @@ export function ThemeProvider({ ...props }: ThemeProviderProps) { const [theme, setTheme] = useState( - () => (localStorage.getItem(storageKey) as Theme) || defaultTheme, + () => + (typeof localStorage !== "undefined" + ? (localStorage.getItem(storageKey) as Theme) + : null) || defaultTheme, ) const getResolvedTheme = useCallback((theme: Theme): "dark" | "light" => { if (theme === "system") { + // Prerendered HTML has no preference to read; the pre-paint script in + // the document head resolves it before React ever hydrates. + if (typeof window === "undefined") return "light" return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"