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