11import { createContext , useContext , useEffect , useState } from "react"
22
33type Theme = "dark" | "light" | "system"
4+ type ResolvedTheme = Exclude < Theme , "system" >
45
56type ThemeProviderState = {
67 theme : Theme
8+ resolvedTheme : ResolvedTheme
79 setTheme : ( theme : Theme ) => void
810}
911
1012const ThemeProviderContext = createContext < ThemeProviderState > ( {
1113 theme : "system" ,
14+ resolvedTheme : "light" ,
1215 setTheme : ( ) => null ,
1316} )
1417
18+ const MEDIA_QUERY = "(prefers-color-scheme: dark)"
19+
20+ const getSystemTheme = ( ) : ResolvedTheme =>
21+ typeof window !== "undefined" && window . matchMedia ( MEDIA_QUERY ) . matches ? "dark" : "light"
22+
1523export function ThemeProvider ( {
1624 children,
1725 defaultTheme = "system" ,
@@ -24,23 +32,28 @@ export function ThemeProvider({
2432 const [ theme , setTheme ] = useState < Theme > (
2533 ( ) => ( localStorage . getItem ( storageKey ) as Theme ) || defaultTheme
2634 )
35+ const [ systemTheme , setSystemTheme ] = useState < ResolvedTheme > ( getSystemTheme )
36+
37+ useEffect ( ( ) => {
38+ const mediaQuery = window . matchMedia ( MEDIA_QUERY )
39+ const updateSystemTheme = ( ) => setSystemTheme ( mediaQuery . matches ? "dark" : "light" )
40+
41+ updateSystemTheme ( )
42+ mediaQuery . addEventListener ( "change" , updateSystemTheme )
43+
44+ return ( ) => mediaQuery . removeEventListener ( "change" , updateSystemTheme )
45+ } , [ ] )
2746
2847 useEffect ( ( ) => {
2948 const root = window . document . documentElement
3049 root . classList . remove ( "light" , "dark" )
3150
32- if ( theme === "system" ) {
33- const systemTheme = window . matchMedia ( "(prefers-color-scheme: dark)" ) . matches
34- ? "dark"
35- : "light"
36- root . classList . add ( systemTheme )
37- } else {
38- root . classList . add ( theme )
39- }
40- } , [ theme ] )
51+ root . classList . add ( theme === "system" ? systemTheme : theme )
52+ } , [ theme , systemTheme ] )
4153
4254 const value = {
4355 theme,
56+ resolvedTheme : theme === "system" ? systemTheme : theme ,
4457 setTheme : ( theme : Theme ) => {
4558 localStorage . setItem ( storageKey , theme )
4659 setTheme ( theme )
0 commit comments