@@ -22,7 +22,7 @@ function subscribeToSystemColorModeChange(onChange: (newSystemColorMode: ColorMo
22
22
return subscribeToMedia ( "(prefers-color-scheme: dark)" , ( ) => onChange ( getSystemColorMode ( ) ) ) ;
23
23
}
24
24
25
- const ColorModeStorageKey = "theme" ;
25
+ const ColorModeStorageKey = "supertokens- theme" ;
26
26
27
27
const storage = {
28
28
get : ( ) : string | null => {
@@ -60,12 +60,9 @@ const storage = {
60
60
} ;
61
61
62
62
const DefaultColorMode : ColorMode = "dark" ;
63
- // We use data-theme-choice="system", not an absent attribute
64
63
const SystemAttribute = "system" ;
65
64
66
- // Ensure to always return a valid colorMode even if input is invalid
67
65
const coerceToColorMode = ( colorMode : string | null ) : ColorMode => ( colorMode === "dark" ? "dark" : "light" ) ;
68
-
69
66
const coerceToColorModeChoice = ( colorMode : string | null ) : ColorModeChoice =>
70
67
colorMode === null || colorMode === SystemAttribute ? null : coerceToColorMode ( colorMode ) ;
71
68
@@ -95,7 +92,8 @@ const persistColorModeChoice = (newColorMode: ColorModeChoice) => {
95
92
if ( newColorMode === null ) {
96
93
storage . del ( ) ;
97
94
} else {
98
- storage . set ( coerceToColorMode ( newColorMode ) ) ;
95
+ const coercedValue = coerceToColorMode ( newColorMode ) ;
96
+ storage . set ( coercedValue ) ;
99
97
}
100
98
} ;
101
99
@@ -116,21 +114,35 @@ class ThemeStore {
116
114
colorMode : DefaultColorMode ,
117
115
colorModeChoice : null ,
118
116
} ;
119
-
120
- if ( typeof window !== "undefined" ) {
121
- this . initialize ( ) ;
122
- }
123
117
}
124
118
125
119
private initialize ( ) {
126
120
if ( this . initialized ) return ;
127
121
this . initialized = true ;
128
122
123
+ const storedValue = storage . get ( ) ;
124
+ const storedChoice = coerceToColorModeChoice ( storedValue ) ;
125
+
126
+ let colorModeChoice : ColorModeChoice ;
127
+ let colorMode : ColorMode ;
128
+
129
+ if ( storedChoice !== null ) {
130
+ colorModeChoice = storedChoice ;
131
+ colorMode = storedChoice ;
132
+ } else {
133
+ colorModeChoice = ColorModeChoiceAttribute . get ( ) ;
134
+ colorMode = colorModeChoice !== null ? colorModeChoice : getSystemColorMode ( ) ;
135
+ }
136
+
129
137
this . state = {
130
- colorMode : ColorModeAttribute . get ( ) ,
131
- colorModeChoice : ColorModeChoiceAttribute . get ( ) ,
138
+ colorMode,
139
+ colorModeChoice,
132
140
} ;
133
141
142
+ // Update DOM attributes to match the restored state
143
+ ColorModeAttribute . set ( colorMode ) ;
144
+ ColorModeChoiceAttribute . set ( colorModeChoice ) ;
145
+
134
146
this . systemChangeUnsubscribe = subscribeToSystemColorModeChange ( ( newSystemColorMode ) => {
135
147
if ( this . state . colorModeChoice === null ) {
136
148
this . updateColorMode ( newSystemColorMode , this . state . colorModeChoice ) ;
@@ -140,7 +152,10 @@ class ThemeStore {
140
152
// Subscribe to storage changes (for cross-tab synchronization)
141
153
this . storageUnsubscribe = storage . listen ( ( e ) => {
142
154
const newChoice = coerceToColorModeChoice ( e . newValue ) ;
143
- this . setColorMode ( newChoice , false ) ;
155
+
156
+ if ( newChoice !== this . state . colorModeChoice ) {
157
+ this . setColorMode ( newChoice , false ) ;
158
+ }
144
159
} ) ;
145
160
}
146
161
@@ -193,39 +208,23 @@ class ThemeStore {
193
208
194
209
const themeStore = new ThemeStore ( ) ;
195
210
196
- export type ThemeOption = ' system' | ' light' | ' dark' ;
211
+ export type ThemeOption = " system" | " light" | " dark" ;
197
212
198
213
export function useTheme ( ) {
199
214
const state = useSyncExternalStore ( themeStore . subscribe , themeStore . getSnapshot , ( ) => ( {
200
215
colorMode : DefaultColorMode ,
201
216
colorModeChoice : null ,
202
217
} ) ) ;
203
218
204
- // Convert internal state to external API
205
- const currentTheme : ThemeOption = state . colorModeChoice === null ? 'system' : state . colorModeChoice ;
206
-
207
219
const setTheme = ( theme : ThemeOption ) => {
208
- const colorModeChoice = theme === ' system' ? null : theme ;
220
+ const colorModeChoice = theme === " system" ? null : theme ;
209
221
themeStore . setColorMode ( colorModeChoice ) ;
210
222
} ;
211
223
212
- const getNextTheme = ( ) : ThemeOption => {
213
- if ( currentTheme === 'system' ) {
214
- // If system, next should be opposite of current system preference
215
- const systemTheme = getSystemColorMode ( ) ;
216
- return systemTheme === 'light' ? 'dark' : 'light' ;
217
- }
218
- if ( currentTheme === 'light' ) return 'dark' ;
219
- return 'system' ; // dark -> system
220
- } ;
221
-
222
224
return {
223
225
theme : state . colorMode ,
224
- currentTheme,
225
226
setTheme,
226
- getNextTheme,
227
227
} ;
228
228
}
229
229
230
230
export { themeStore } ;
231
-
0 commit comments