43 lines
1.1 KiB
TypeScript
43 lines
1.1 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
|
|
type Theme = "dark" | "light";
|
|
|
|
const STORAGE_KEY = "theme";
|
|
|
|
function isTheme(value: string | null): value is Theme {
|
|
return value === "dark" || value === "light";
|
|
}
|
|
|
|
export default function ThemeToggle() {
|
|
const [theme, setTheme] = useState<Theme>("dark");
|
|
|
|
useEffect(() => {
|
|
const storedTheme = localStorage.getItem(STORAGE_KEY);
|
|
const activeTheme: Theme = isTheme(storedTheme) ? storedTheme : "dark";
|
|
|
|
document.documentElement.setAttribute("data-theme", activeTheme);
|
|
setTheme(activeTheme);
|
|
}, []);
|
|
|
|
const handleToggle = () => {
|
|
const nextTheme: Theme = theme === "dark" ? "light" : "dark";
|
|
|
|
setTheme(nextTheme);
|
|
document.documentElement.setAttribute("data-theme", nextTheme);
|
|
localStorage.setItem(STORAGE_KEY, nextTheme);
|
|
};
|
|
|
|
return (
|
|
<button
|
|
type="button"
|
|
onClick={handleToggle}
|
|
className="theme-toggle"
|
|
aria-label={`Switch to ${theme === "dark" ? "light" : "dark"} mode`}
|
|
>
|
|
<span className="theme-toggle-label">{theme === "dark" ? "Light" : "Dark"}</span>
|
|
</button>
|
|
);
|
|
}
|