Files
2026-06-16 19:17:37 +03:00

91 lines
2.3 KiB
TypeScript

"use client"
import * as React from "react"
type Theme = "light" | "dark" | "system"
const ThemeContext = React.createContext<{
theme: Theme
setTheme: (theme: Theme) => void
resolvedTheme: "light" | "dark"
}>({
theme: "system",
setTheme: () => {},
resolvedTheme: "light",
})
export function useTheme() {
return React.useContext(ThemeContext)
}
function getSystemTheme(): "light" | "dark" {
if (typeof window === "undefined") return "light"
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"
}
export function ThemeProvider({
children,
attribute = "class",
defaultTheme = "system",
enableSystem = true,
}: {
children: React.ReactNode
attribute?: string
defaultTheme?: Theme
enableSystem?: boolean
disableTransitionOnChange?: boolean
}) {
const [theme, setThemeState] = React.useState<Theme>(defaultTheme)
const [resolvedTheme, setResolvedTheme] = React.useState<"light" | "dark">("light")
const [mounted, setMounted] = React.useState(false)
// Read saved theme from localStorage on mount
React.useEffect(() => {
const saved = localStorage.getItem("theme") as Theme | null
if (saved) {
setThemeState(saved)
}
setMounted(true)
}, [])
// Resolve system theme and apply to document
React.useEffect(() => {
const resolved = theme === "system" ? getSystemTheme() : theme
setResolvedTheme(resolved)
const root = document.documentElement
root.classList.remove("light", "dark")
root.classList.add(resolved)
}, [theme])
// Listen for system theme changes
React.useEffect(() => {
if (!enableSystem) return
const mql = window.matchMedia("(prefers-color-scheme: dark)")
const handler = () => {
if (theme === "system") {
const resolved = getSystemTheme()
setResolvedTheme(resolved)
const root = document.documentElement
root.classList.remove("light", "dark")
root.classList.add(resolved)
}
}
mql.addEventListener("change", handler)
return () => mql.removeEventListener("change", handler)
}, [theme, enableSystem])
const setTheme = React.useCallback((newTheme: Theme) => {
setThemeState(newTheme)
localStorage.setItem("theme", newTheme)
}, [])
return (
<ThemeContext.Provider value={{ theme, setTheme, resolvedTheme }}>
{children}
</ThemeContext.Provider>
)
}