UNPKG

@flanksource/clicky-ui

Version:

Flanksource Clicky UI — React component library built on shadcn/ui with light/dark and density theming.

69 lines (68 loc) 2.32 kB
import { jsx } from "react/jsx-runtime"; import { useContext, createContext, useState, useEffect, useCallback, useMemo } from "react"; const STORAGE_KEY = "clicky-ui-theme"; const DATA_ATTR = "data-theme"; const ThemeContext = createContext(null); function prefersDark() { if (typeof window === "undefined") return false; return window.matchMedia("(prefers-color-scheme: dark)").matches; } function readStored() { if (typeof window === "undefined") return "system"; const raw = window.localStorage.getItem(STORAGE_KEY); return raw === "light" || raw === "dark" || raw === "system" ? raw : "system"; } function resolve(theme) { if (theme === "system") return prefersDark() ? "dark" : "light"; return theme; } function apply(resolved) { if (typeof document === "undefined") return; document.documentElement.setAttribute(DATA_ATTR, resolved); } function ThemeProvider({ children, defaultTheme = "system", storageKey = STORAGE_KEY }) { const [theme, setThemeState] = useState(() => { if (typeof window === "undefined") return defaultTheme; return readStored() ?? defaultTheme; }); const [resolvedTheme, setResolvedTheme] = useState(() => resolve(theme)); useEffect(() => { const next = resolve(theme); setResolvedTheme(next); apply(next); if (typeof window !== "undefined") { window.localStorage.setItem(storageKey, theme); } }, [theme, storageKey]); useEffect(() => { if (theme !== "system" || typeof window === "undefined") return; const mq = window.matchMedia("(prefers-color-scheme: dark)"); const onChange = () => { const next = mq.matches ? "dark" : "light"; setResolvedTheme(next); apply(next); }; mq.addEventListener("change", onChange); return () => mq.removeEventListener("change", onChange); }, [theme]); const setTheme = useCallback((next) => setThemeState(next), []); const value = useMemo( () => ({ theme, resolvedTheme, setTheme }), [theme, resolvedTheme, setTheme] ); return /* @__PURE__ */ jsx(ThemeContext.Provider, { value, children }); } function useTheme() { const ctx = useContext(ThemeContext); if (!ctx) throw new Error("useTheme must be used inside <ThemeProvider>"); return ctx; } export { ThemeProvider, useTheme }; //# sourceMappingURL=use-theme.js.map