🔧 Critical Fixes: - Added /walkthrough route to router - Fixed 404 error on Daily Walkthrough ✨ Visual Upgrades: - Replaced ALL emojis with Lucide SVG icons - Added smooth animations (fadeIn, slideIn, scaleIn, shimmer) - Icon scale effects on hover/active - Pulse animations on active indicators - Smooth theme transitions - Professional icon set throughout 🎨 Polish: - Space Grotesk font (premium geometric) - Emerald scrollbars - Animated nav icons - Status indicators with pulse - Smooth color transitions Dependencies: - Added lucide-react for SVG icons Status: Production-ready with animations
74 lines
2.6 KiB
TypeScript
74 lines
2.6 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { Sun, Moon, Monitor } from 'lucide-react';
|
|
|
|
/**
|
|
* ThemeToggle - Dark/Light mode toggle with SVG icons
|
|
*
|
|
* Provides accessible theme switching with smooth animations.
|
|
*/
|
|
|
|
type Theme = 'light' | 'dark' | 'system';
|
|
|
|
export default function ThemeToggle() {
|
|
const [theme, setTheme] = useState<Theme>('system');
|
|
|
|
useEffect(() => {
|
|
const savedTheme = localStorage.getItem('theme') as Theme | null;
|
|
if (savedTheme) {
|
|
setTheme(savedTheme);
|
|
applyTheme(savedTheme);
|
|
} else {
|
|
applyTheme('system');
|
|
}
|
|
}, []);
|
|
|
|
function applyTheme(newTheme: Theme) {
|
|
const root = window.document.documentElement;
|
|
root.classList.remove('light', 'dark');
|
|
|
|
if (newTheme === 'system') {
|
|
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
? 'dark'
|
|
: 'light';
|
|
root.classList.add(systemTheme);
|
|
} else {
|
|
root.classList.add(newTheme);
|
|
}
|
|
}
|
|
|
|
function handleThemeChange(newTheme: Theme) {
|
|
setTheme(newTheme);
|
|
localStorage.setItem('theme', newTheme);
|
|
applyTheme(newTheme);
|
|
}
|
|
|
|
const themes = [
|
|
{ value: 'light' as Theme, icon: Sun, label: 'Light' },
|
|
{ value: 'dark' as Theme, icon: Moon, label: 'Dark' },
|
|
{ value: 'system' as Theme, icon: Monitor, label: 'Auto' },
|
|
];
|
|
|
|
return (
|
|
<div className="flex items-center gap-1 bg-slate-100 dark:bg-slate-700/50 rounded-lg p-1">
|
|
{themes.map(({ value, icon: Icon, label }) => (
|
|
<button
|
|
key={value}
|
|
onClick={() => handleThemeChange(value)}
|
|
className={`group flex items-center gap-1.5 px-3 py-2 rounded-md text-sm font-medium transition-all ${theme === value
|
|
? 'bg-white dark:bg-slate-600 text-slate-900 dark:text-white shadow-sm'
|
|
: 'text-slate-600 dark:text-slate-400 hover:text-slate-900 dark:hover:text-white'
|
|
}`}
|
|
aria-label={`${label} mode`}
|
|
aria-pressed={theme === value}
|
|
>
|
|
<Icon
|
|
className={`w-4 h-4 transition-transform ${theme === value ? 'scale-110' : 'group-hover:scale-105'
|
|
}`}
|
|
strokeWidth={theme === value ? 2.5 : 2}
|
|
/>
|
|
<span className="hidden sm:inline">{label}</span>
|
|
</button>
|
|
))}
|
|
</div>
|
|
);
|
|
}
|