From 1776ea5d5964499a09d0f8872ace3370d2f1ccaa 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. Co-Authored-By: Claude Opus 5 (1M context) --- 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"