fix: eliminate theme flickering on page load
Fixed FOUC (Flash of Unstyled Content) issue with dark mode: **Problem:** - Page would flash light theme before switching to dark - localStorage check happened after React hydration - Caused jarring visual experience **Solution:** - Added blocking script in <head> that runs immediately - Checks localStorage and system preference synchronously - Applies 'dark' class before any rendering - Prevents flash completely **Technical Changes:** - Inline script in layout.tsx head - Runs before React hydration - Added suppressHydrationWarning to html tag - ThemeToggle now reads from DOM instead of re-setting - Wrapped in try-catch for safety **Result:** - No more flickering on page load - Smooth theme experience - Works with both saved preference and system default - Instant dark mode application 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -20,7 +20,26 @@ export default function RootLayout({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en" suppressHydrationWarning>
|
||||||
|
<head>
|
||||||
|
<script
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
(function() {
|
||||||
|
try {
|
||||||
|
const theme = localStorage.getItem('theme');
|
||||||
|
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
|
const shouldBeDark = theme === 'dark' || (!theme && prefersDark);
|
||||||
|
|
||||||
|
if (shouldBeDark) {
|
||||||
|
document.documentElement.classList.add('dark');
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
})();
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
<body className="antialiased">
|
<body className="antialiased">
|
||||||
<ToastProvider>
|
<ToastProvider>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -15,13 +15,9 @@ export function ThemeToggle() {
|
|||||||
}, [theme]);
|
}, [theme]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
// Check for saved theme preference or default to light
|
// Read the current theme from the DOM (set by blocking script)
|
||||||
const savedTheme = localStorage.getItem('theme') as 'light' | 'dark' | null;
|
const isDark = document.documentElement.classList.contains('dark');
|
||||||
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
setTheme(isDark ? 'dark' : 'light');
|
||||||
const initialTheme = savedTheme || (prefersDark ? 'dark' : 'light');
|
|
||||||
|
|
||||||
setTheme(initialTheme);
|
|
||||||
document.documentElement.classList.toggle('dark', initialTheme === 'dark');
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Keyboard shortcut: Ctrl/Cmd + D
|
// Keyboard shortcut: Ctrl/Cmd + D
|
||||||
|
|||||||
Reference in New Issue
Block a user