Files
figlet-ui/components/layout/ThemeToggle.tsx
Sebastian Krüger 7ef4ea026e 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>
2025-11-09 13:34:49 +01:00

52 lines
1.4 KiB
TypeScript

'use client';
import * as React from 'react';
import { Moon, Sun } from 'lucide-react';
import { Button } from '@/components/ui/Button';
export function ThemeToggle() {
const [theme, setTheme] = React.useState<'light' | 'dark'>('light');
const toggleTheme = React.useCallback(() => {
const newTheme = theme === 'light' ? 'dark' : 'light';
setTheme(newTheme);
localStorage.setItem('theme', newTheme);
document.documentElement.classList.toggle('dark', newTheme === 'dark');
}, [theme]);
React.useEffect(() => {
// Read the current theme from the DOM (set by blocking script)
const isDark = document.documentElement.classList.contains('dark');
setTheme(isDark ? 'dark' : 'light');
}, []);
// Keyboard shortcut: Ctrl/Cmd + D
React.useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'd' && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
toggleTheme();
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [toggleTheme]);
return (
<Button
variant="outline"
size="icon"
onClick={toggleTheme}
aria-label="Toggle theme"
title={`Switch to ${theme === 'light' ? 'dark' : 'light'} mode`}
>
{theme === 'light' ? (
<Moon className="h-4 w-4" />
) : (
<Sun className="h-4 w-4" />
)}
</Button>
);
}