Skip to content

Commit c38321c

Browse files
committed
Fix theme switcher
1 parent 2e48cd2 commit c38321c

File tree

4 files changed

+35
-37
lines changed

4 files changed

+35
-37
lines changed

src/css/radix.css

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -766,16 +766,12 @@
766766
flex-direction: column;
767767
}
768768

769-
.light,
770-
.light-theme,
771-
:is(.light, .light-theme) :where(.radix-themes:not(.dark, .dark-theme)) {
769+
:root {
772770
--color-background: #ffffff;
773771
--code-font-family: "IBM Plex Mono", monospace;
774772
}
775773

776-
.dark,
777-
.dark-theme,
778-
:is(.dark, .dark-theme) :where(.radix-themes:not(.light, .light-theme)) {
774+
[data-theme="dark"] {
779775
--color-background: #0a0a0a;
780776
--code-font-family: "IBM Plex Mono", monospace;
781777
}

src/lib/themeStore.ts

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ function subscribeToSystemColorModeChange(onChange: (newSystemColorMode: ColorMo
2222
return subscribeToMedia("(prefers-color-scheme: dark)", () => onChange(getSystemColorMode()));
2323
}
2424

25-
const ColorModeStorageKey = "theme";
25+
const ColorModeStorageKey = "supertokens-theme";
2626

2727
const storage = {
2828
get: (): string | null => {
@@ -60,12 +60,9 @@ const storage = {
6060
};
6161

6262
const DefaultColorMode: ColorMode = "dark";
63-
// We use data-theme-choice="system", not an absent attribute
6463
const SystemAttribute = "system";
6564

66-
// Ensure to always return a valid colorMode even if input is invalid
6765
const coerceToColorMode = (colorMode: string | null): ColorMode => (colorMode === "dark" ? "dark" : "light");
68-
6966
const coerceToColorModeChoice = (colorMode: string | null): ColorModeChoice =>
7067
colorMode === null || colorMode === SystemAttribute ? null : coerceToColorMode(colorMode);
7168

@@ -95,7 +92,8 @@ const persistColorModeChoice = (newColorMode: ColorModeChoice) => {
9592
if (newColorMode === null) {
9693
storage.del();
9794
} else {
98-
storage.set(coerceToColorMode(newColorMode));
95+
const coercedValue = coerceToColorMode(newColorMode);
96+
storage.set(coercedValue);
9997
}
10098
};
10199

@@ -116,21 +114,35 @@ class ThemeStore {
116114
colorMode: DefaultColorMode,
117115
colorModeChoice: null,
118116
};
119-
120-
if (typeof window !== "undefined") {
121-
this.initialize();
122-
}
123117
}
124118

125119
private initialize() {
126120
if (this.initialized) return;
127121
this.initialized = true;
128122

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+
129137
this.state = {
130-
colorMode: ColorModeAttribute.get(),
131-
colorModeChoice: ColorModeChoiceAttribute.get(),
138+
colorMode,
139+
colorModeChoice,
132140
};
133141

142+
// Update DOM attributes to match the restored state
143+
ColorModeAttribute.set(colorMode);
144+
ColorModeChoiceAttribute.set(colorModeChoice);
145+
134146
this.systemChangeUnsubscribe = subscribeToSystemColorModeChange((newSystemColorMode) => {
135147
if (this.state.colorModeChoice === null) {
136148
this.updateColorMode(newSystemColorMode, this.state.colorModeChoice);
@@ -140,7 +152,10 @@ class ThemeStore {
140152
// Subscribe to storage changes (for cross-tab synchronization)
141153
this.storageUnsubscribe = storage.listen((e) => {
142154
const newChoice = coerceToColorModeChoice(e.newValue);
143-
this.setColorMode(newChoice, false);
155+
156+
if (newChoice !== this.state.colorModeChoice) {
157+
this.setColorMode(newChoice, false);
158+
}
144159
});
145160
}
146161

@@ -193,39 +208,23 @@ class ThemeStore {
193208

194209
const themeStore = new ThemeStore();
195210

196-
export type ThemeOption = 'system' | 'light' | 'dark';
211+
export type ThemeOption = "system" | "light" | "dark";
197212

198213
export function useTheme() {
199214
const state = useSyncExternalStore(themeStore.subscribe, themeStore.getSnapshot, () => ({
200215
colorMode: DefaultColorMode,
201216
colorModeChoice: null,
202217
}));
203218

204-
// Convert internal state to external API
205-
const currentTheme: ThemeOption = state.colorModeChoice === null ? 'system' : state.colorModeChoice;
206-
207219
const setTheme = (theme: ThemeOption) => {
208-
const colorModeChoice = theme === 'system' ? null : theme;
220+
const colorModeChoice = theme === "system" ? null : theme;
209221
themeStore.setColorMode(colorModeChoice);
210222
};
211223

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-
222224
return {
223225
theme: state.colorMode,
224-
currentTheme,
225226
setTheme,
226-
getNextTheme,
227227
};
228228
}
229229

230230
export { themeStore };
231-

src/theme/Logo/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export default function Logo(props: Props): JSX.Element {
3434
const src = theme === "dark" ? "/img/logos/supertokens-light.svg" : "/img/logos/supertokens-dark.svg";
3535

3636
return (
37-
<Link to="https://supertokens.com" target="_blank" className="navbar__brand">
37+
<Link to="https://supertokens.com" className="navbar__brand">
3838
<img src={src} className="navbar__logo" alt="SuperTokens" />
3939
</Link>
4040
);

src/theme/Root.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { init, getCookieConsent, setCookieConsent, trackPageExit, trackPageView
66
import { useCallback, useEffect, useRef, useState } from "react";
77
import { useLocation } from "@docusaurus/router";
88
import { useTheme } from "../lib";
9+
import useLayoutEffect from "@docusaurus/useIsomorphicLayoutEffect";
910

1011
export default function Root({ children }: { children: React.ReactNode }) {
1112
const { pathname } = useLocation();
@@ -17,6 +18,8 @@ export default function Root({ children }: { children: React.ReactNode }) {
1718
init();
1819
}, []);
1920

21+
useLayoutEffect(() => {}, []);
22+
2023
useEffect(() => {
2124
trackPageView();
2225
}, [pathname]);

0 commit comments

Comments
 (0)