@@ -30,13 +30,6 @@ function getModeTheme(theme, mode) {
3030const getMediaQuery = query => `@media ${ query } `
3131const getColorModeQuery = mode => `(prefers-color-scheme: ${ mode } )`
3232
33- function detectSystemMode ( mode ) {
34- if ( window . matchMedia === undefined ) return null
35- const query = getColorModeQuery ( mode )
36- const mql = window . matchMedia ( query )
37- return mql . matches && mql . media === query
38- }
39-
4033function hasColorModes ( theme ) {
4134 return theme && theme . colors && theme . colors . modes
4235}
@@ -101,55 +94,96 @@ export function createColorStyles(theme, { targetSelector = 'body' } = {}) {
10194 return `${ targetSelector } {${ styles } }`
10295}
10396
97+ function getSystemModeMql ( mode ) {
98+ if ( window . matchMedia === undefined ) return null
99+ const query = getColorModeQuery ( mode )
100+ return window . matchMedia ( query )
101+ }
102+
104103function useSystemMode ( theme ) {
105- return React . useMemo ( ( ) => {
106- if ( ! hasColorModes ( theme ) || ! hasMediaQueryEnabled ( theme ) ) return null
107- return (
108- SYSTEM_MODES . find ( mode => {
109- if ( ! theme . colors . modes [ mode ] ) return null
110- return detectSystemMode ( mode )
111- } ) || null
112- )
104+ const configs = React . useMemo ( ( ) => {
105+ if ( ! hasMediaQueryEnabled ( theme ) ) return [ ]
106+ return SYSTEM_MODES . map ( mode => {
107+ if ( ! theme . colors . modes [ mode ] ) return null
108+ const mql = getSystemModeMql ( mode )
109+ return mql ? { mode, mql } : null
110+ } ) . filter ( Boolean )
113111 } , [ theme ] )
112+
113+ const [ systemMode , setSystemMode ] = React . useState ( ( ) => {
114+ const config = configs . find ( config => config . mql . matches )
115+ return config ? config . mode : null
116+ } )
117+
118+ React . useEffect ( ( ) => {
119+ const cleans = configs
120+ . filter ( ( { mql } ) => mql . addListener && mql . removeListener )
121+ . map ( ( { mode, mql } ) => {
122+ const handler = ( { matches } ) => {
123+ if ( matches ) {
124+ setSystemMode ( mode )
125+ } else {
126+ setSystemMode ( previousMode => ( previousMode === mode ? null : mode ) )
127+ }
128+ }
129+ mql . addListener ( handler )
130+ return ( ) => mql . removeListener ( handler )
131+ } )
132+ return ( ) => cleans . forEach ( clean => clean ( ) )
133+ } )
134+
135+ return systemMode
114136}
115137
116138export function useColorModeState ( theme , { target = document . body } = { } ) {
117139 const systemMode = useSystemMode ( theme )
140+ const defaultColorMode = getDefaultColorModeName ( theme )
141+ const initialColorMode = getInitialColorModeName ( theme )
118142 const [ mode , setMode ] = React . useState ( ( ) => {
119143 if ( ! hasColorModes ( theme ) ) return null
120144 const storedMode = storage . get ( )
121- return storedMode || systemMode || getDefaultColorModeName ( theme )
145+ return storedMode || systemMode || defaultColorMode
122146 } )
123147
124148 // Add mode className
125149 const customPropertiesEnabled = hasCustomPropertiesEnabled ( theme )
126150
151+ const manualSetRef = React . useRef ( false )
152+ const manuallySetMode = React . useCallback ( value => {
153+ manualSetRef . current = true
154+ setMode ( value )
155+ } , [ ] )
156+
127157 // Store mode preference
128- const changedRef = React . useRef ( false )
129- React . useEffect ( ( ) => {
130- if ( changedRef . current ) {
158+ React . useLayoutEffect ( ( ) => {
159+ if ( manualSetRef . current ) {
131160 storage . set ( mode )
132- } else {
133- changedRef . current = true
134161 }
135162 } , [ mode ] )
136163
137- const initialMode = getInitialColorModeName ( theme )
138-
139- React . useEffect ( ( ) => {
140- if ( ! customPropertiesEnabled ) return undefined
164+ // Sync system mode
165+ React . useLayoutEffect ( ( ) => {
141166 const storedMode = storage . get ( )
142- const fromSystem = ! storedMode && systemMode === mode
143- const initial = ! storedMode && initialMode === mode
144- if ( fromSystem || initial ) return undefined
167+ if ( storedMode ) return
168+ const targetMode = systemMode || defaultColorMode
169+ if ( targetMode === mode ) return
170+ setMode ( targetMode )
171+ } , [ mode , systemMode , defaultColorMode ] )
172+
173+ // Add and remove class names
174+ React . useLayoutEffect ( ( ) => {
175+ if ( ! customPropertiesEnabled ) return undefined
176+ const stored = storage . get ( )
177+ const initial = initialColorMode !== mode
178+ if ( ! stored && ! initial ) return undefined
145179 const className = getColorModeClassName ( mode )
146180 target . classList . add ( className )
147181 return ( ) => {
148182 target . classList . remove ( className )
149183 }
150- } , [ customPropertiesEnabled , target , mode , systemMode , initialMode ] )
184+ } , [ customPropertiesEnabled , target , mode , initialColorMode ] )
151185
152- return [ mode , setMode ]
186+ return [ mode , manuallySetMode ]
153187}
154188
155189export function useColorModeTheme ( theme , mode ) {
0 commit comments