Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | 4x 4x 4x 26x 26x 26x 26x | "use client";
import { useEffect, useState } from "react";
import { Sun, Moon } from "lucide-react";
const THEME_KEY = "theme";
const THEME_CHANGE_EVENT = "theme-change";
/** Broadcast theme changes across components in the same tab. */
export const themeChangeTarget = new EventTarget();
export default function ThemeToggle() {
const [isDark, setIsDark] = useState(false);
useEffect(() => {
// Sync initial state from html class (set by inline script in layout.tsx)
setIsDark(document.documentElement.classList.contains("dark"));
}, []);
function toggle() {
const next = !isDark;
setIsDark(next);
document.documentElement.classList.toggle("dark", next);
try {
localStorage.setItem(THEME_KEY, next ? "dark" : "light");
} catch (_e) {
// storage blocked
}
themeChangeTarget.dispatchEvent(
new CustomEvent(THEME_CHANGE_EVENT, { detail: { dark: next } }),
);
}
return (
<button
onClick={toggle}
aria-label={isDark ? "Switch to light mode" : "Switch to dark mode"}
title={isDark ? "Switch to light mode" : "Switch to dark mode"}
className="p-2 rounded-md touch-target-sm text-[var(--text-secondary)] hover:text-[var(--text-primary)] hover:bg-[var(--surface-2)] transition-colors"
>
{isDark ? (
<Sun size={18} aria-hidden="true" />
) : (
<Moon size={18} aria-hidden="true" />
)}
</button>
);
}
|