@@ -5,53 +5,76 @@ import lighttheme, { darktheme } from "./src/theme/app/themeStyles";
55const themes = { light : lighttheme , dark : darktheme } ;
66
77const MagicScriptTag = ( props ) => {
8+ // FIX: Stringify the theme object outside the template literal to prevent syntax errors caused by unescaped quotes inside theme values.
9+ const themeJSON = JSON . stringify ( props . theme ) ;
10+
11+ // Injects CSS variables and theme state strictly before the first paint to prevent FOUC.
812 const codeToRunOnClient = `
913 (function() {
10- // 1. Keeps SYSTEM as the priority preference
11- const themeFromLocalStorage = localStorage.getItem('${ DarkThemeKey } ') || '${ ThemeSetting . SYSTEM } ';
14+ try {
15+ // 1. Keeps SYSTEM as the priority preference
16+ const themeFromLocalStorage = localStorage.getItem('${ DarkThemeKey } ') || '${ ThemeSetting . SYSTEM } ';
1217
13- // 2. We change the check to look for LIGHT mode explicitly
14- const systemLightModeSetting = () => window.matchMedia ? window.matchMedia('(prefers-color-scheme: light)') : null;
15-
16- const isLightModeActive = () => {
17- return !!systemLightModeSetting()?.matches;
18- };
18+ // 2. We change the check to look for LIGHT mode explicitly
19+ const systemLightModeSetting = () => window.matchMedia ? window.matchMedia('(prefers-color-scheme: light)') : null;
20+
21+ const isLightModeActive = () => {
22+ return !!systemLightModeSetting()?.matches;
23+ };
1924
20- let colorMode;
21- switch (themeFromLocalStorage) {
22- case '${ ThemeSetting . SYSTEM } ':
23- // LOGIC CHANGE: If Light is active -> Light. Otherwise (Dark, No Preference, or Error) -> Dark.
24- colorMode = isLightModeActive() ? '${ ThemeSetting . LIGHT } ' : '${ ThemeSetting . DARK } '
25- break
26- case '${ ThemeSetting . DARK } ':
27- case '${ ThemeSetting . LIGHT } ':
28- colorMode = themeFromLocalStorage
29- break
30- default:
31- // 3. Fallback to DARK in case of error
32- colorMode = '${ ThemeSetting . DARK } '
33- }
25+ let colorMode;
26+ switch (themeFromLocalStorage) {
27+ case '${ ThemeSetting . SYSTEM } ':
28+ // LOGIC CHANGE: If Light is active -> Light. Otherwise (Dark, No Preference, or Error) -> Dark.
29+ colorMode = isLightModeActive() ? '${ ThemeSetting . LIGHT } ' : '${ ThemeSetting . DARK } ';
30+ break;
31+ case '${ ThemeSetting . DARK } ':
32+ case '${ ThemeSetting . LIGHT } ':
33+ colorMode = themeFromLocalStorage;
34+ break;
35+ default:
36+ // 3. Fallback to DARK in case of error
37+ colorMode = '${ ThemeSetting . DARK } ';
38+ }
3439
35- const root = document.documentElement;
36- const iterate = (obj) => {
37- if (!obj) return;
38- Object.keys(obj).forEach(key => {
39- if (typeof obj[key] === 'object') {
40- iterate(obj[key])
41- } else {
42- root.style.setProperty("--" + key, obj[key])
40+ const root = document.documentElement;
41+ const iterate = (obj) => {
42+ if (!obj) return;
43+ Object.keys(obj).forEach(key => {
44+ if (typeof obj[key] === 'object') {
45+ iterate(obj[key]);
46+ } else {
47+ root.style.setProperty("--" + key, obj[key]);
48+ }
49+ });
50+ };
51+
52+ // FIX: Inject the JSON object directly to avoid JSON.parse breaking on nested quotes.
53+ const parsedTheme = ${ themeJSON } ;
54+ const theme = parsedTheme[colorMode];
55+
56+ if (theme) {
57+ iterate(theme);
4358 }
44- })
59+
60+ root.style.setProperty('--initial-color-mode', colorMode);
61+
62+ // FIX: Setting data-theme is required for global CSS styles to apply correctly before React hydration.
63+ root.setAttribute('data-theme', colorMode);
64+
65+ // Sync the calculated theme globally so ThemeManager can pick it up seamlessly.
66+ window.__theme = colorMode;
67+
68+ } catch (e) {
69+ console.error('Dark mode injection failed:', e);
4570 }
46- const parsedTheme = JSON.parse('${ JSON . stringify ( props . theme ) } ')
47- const theme = parsedTheme[colorMode]
48- iterate(theme)
49- root.style.setProperty('--initial-color-mode', colorMode);
50- })()
71+ })();
5172 ` ;
5273 return < script dangerouslySetInnerHTML = { { __html : codeToRunOnClient } } /> ;
5374} ;
5475
55- export const onRenderBody = ( { setPreBodyComponents } ) => {
56- setPreBodyComponents ( < MagicScriptTag key = "theme-injection" theme = { themes } /> ) ;
57- } ;
76+ // FIX: Using setHeadComponents instead of setPreBodyComponents ensures the script runs
77+ // strictly in the <head>, blocking the first paint until the theme is applied and completely eliminating FOUC.
78+ export const onRenderBody = ( { setHeadComponents } ) => {
79+ setHeadComponents ( [ < MagicScriptTag key = "theme-injection" theme = { themes } /> ] ) ;
80+ } ;
0 commit comments