"use client"; import { useState, useSyncExternalStore } from "react"; import { Switcher } from "@unlocalhosted/metalui"; import { THEME_COOKIE, colorwayOf, type Theme } from "@/lib/theme"; const options = [ { value: "light", label: "Bone" }, { value: "system", label: "Auto" }, { value: "dark", label: "Graphite" }, ] satisfies { value: Theme; label: string }[]; function applyTheme(theme: Theme) { const root = document.documentElement; const colorway = colorwayOf(theme); if (colorway) root.dataset.muColorway = colorway; else delete root.dataset.muColorway; document.cookie = `${THEME_COOKIE}=${theme}; path=/; max-age=31536000; samesite=lax`; } /** Bone / auto / graphite, as a compact switcher. The choice is stored in a cookie so the server renders it without a flash. */ export function ThemeToggle({ initial }: { initial: Theme }) { const [theme, setTheme] = useState(initial); return ( aria-label="Colorway" size="compact" className="w-fit" value={theme} onValueChange={(next) => { setTheme(next); applyTheme(next); }} options={options} /> ); } function subscribe(onChange: () => void) { const mq = window.matchMedia("(prefers-color-scheme: dark)"); const observer = new MutationObserver(onChange); observer.observe(document.documentElement, { attributes: true, attributeFilter: ["data-mu-colorway"] }); mq.addEventListener("change", onChange); return () => { observer.disconnect(); mq.removeEventListener("change", onChange); }; } function isDark() { const forced = document.documentElement.dataset.muColorway; return forced ? forced === "graphite" : window.matchMedia("(prefers-color-scheme: dark)").matches; } /** Whether the page is currently in the graphite colorway, honoring the switcher. */ export function useDarkMode() { return useSyncExternalStore(subscribe, isDark, () => false); }