Files
github-profile-readme-gener…/src/components/ui/theme-toggle.tsx
T
2025-10-14 10:20:13 +05:30

60 lines
1.7 KiB
TypeScript

'use client';
import { useEffect, useState } from 'react';
import { Sun, Moon, Monitor } from 'lucide-react';
import { useTheme } from '@/hooks/use-theme';
import type { ThemeMode } from '@/types/theme';
export function ThemeToggle() {
const { theme, setTheme } = useTheme();
const [mounted, setMounted] = useState(false);
// Prevent hydration mismatch by only rendering after mount
useEffect(() => {
setMounted(true);
}, []);
const handleToggle = () => {
const modes: ThemeMode[] = ['light', 'dark', 'system'];
const currentIndex = modes.indexOf(theme);
const nextIndex = (currentIndex + 1) % modes.length;
setTheme(modes[nextIndex]);
};
const getIcon = () => {
switch (theme) {
case 'light':
return <Sun className="h-5 w-5" />;
case 'dark':
return <Moon className="h-5 w-5" />;
case 'system':
return <Monitor className="h-5 w-5" />;
}
};
// Render a skeleton button during SSR to prevent layout shift
if (!mounted) {
return (
<button
className="border-border bg-card flex h-11 w-11 items-center justify-center rounded-lg border !p-3"
aria-label="Toggle theme"
title="Toggle theme"
disabled
>
<Monitor className="h-5 w-5" />
</button>
);
}
return (
<button
onClick={handleToggle}
className="border-border bg-card hover:bg-accent flex h-11 w-11 items-center justify-center rounded-lg border !p-3 transition-colors"
aria-label={`Switch to ${theme === 'light' ? 'dark' : theme === 'dark' ? 'system' : 'light'} mode`}
title={`Current: ${theme} mode. Click to switch.`}
>
{getIcon()}
</button>
);
}