ca-grow-ops-manager/frontend/src/components/ThemeToggle.tsx
fullsizemalt 9b82e08d34 fix: Add walkthrough route + SVG icons + animations
🔧 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
2025-12-09 14:51:44 -08:00

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>
);
}