56 lines
1.4 KiB
TypeScript
56 lines
1.4 KiB
TypeScript
|
|
'use client';
|
||
|
|
|
||
|
|
import { useEffect, useState } from 'react';
|
||
|
|
import { Moon, Sun } from 'lucide-react';
|
||
|
|
|
||
|
|
export function ThemeToggle() {
|
||
|
|
const [theme, setTheme] = useState<'light' | 'dark'>('light');
|
||
|
|
const [mounted, setMounted] = useState(false);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
setMounted(true);
|
||
|
|
const savedTheme = localStorage.getItem('theme');
|
||
|
|
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||
|
|
const currentTheme = savedTheme === 'dark' || (!savedTheme && prefersDark) ? 'dark' : 'light';
|
||
|
|
setTheme(currentTheme);
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
const toggleTheme = () => {
|
||
|
|
const newTheme = theme === 'light' ? 'dark' : 'light';
|
||
|
|
setTheme(newTheme);
|
||
|
|
localStorage.setItem('theme', newTheme);
|
||
|
|
|
||
|
|
if (newTheme === 'dark') {
|
||
|
|
document.documentElement.classList.add('dark');
|
||
|
|
} else {
|
||
|
|
document.documentElement.classList.remove('dark');
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// Prevent hydration mismatch
|
||
|
|
if (!mounted) {
|
||
|
|
return (
|
||
|
|
<button
|
||
|
|
className="rounded-md p-2 text-muted-foreground"
|
||
|
|
disabled
|
||
|
|
>
|
||
|
|
<Sun className="h-4 w-4" />
|
||
|
|
</button>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<button
|
||
|
|
onClick={toggleTheme}
|
||
|
|
className="rounded-md p-2 hover:bg-accent transition-colors"
|
||
|
|
title={`Switch to ${theme === 'light' ? 'dark' : 'light'} mode`}
|
||
|
|
>
|
||
|
|
{theme === 'light' ? (
|
||
|
|
<Moon className="h-4 w-4" />
|
||
|
|
) : (
|
||
|
|
<Sun className="h-4 w-4" />
|
||
|
|
)}
|
||
|
|
</button>
|
||
|
|
);
|
||
|
|
}
|