Skip to content

Commit f00fabd

Browse files
committed
chore: restore original onRenderBody structure and apply precise FOUC fixes with comments
Signed-off-by: Ankit Rewar <AnkitRewar11@users.noreply.github.com>
1 parent 9ad62cb commit f00fabd

File tree

3 files changed

+20
-21
lines changed

3 files changed

+20
-21
lines changed

onRenderBody.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ import lighttheme, { darktheme } from "./src/theme/app/themeStyles";
55
const themes = { light: lighttheme, dark: darktheme };
66

77
const MagicScriptTag = (props) => {
8-
// Injects CSS variables before first paint. The React hydration mismatch
9-
// is resolved downstream in ThemeManager.js using window.__theme.
8+
// Injects CSS variables and theme state before the first paint to prevent FOUC.
109
const codeToRunOnClient = `
1110
(function() {
1211
// 1. Keeps SYSTEM as the priority preference
@@ -49,12 +48,18 @@ const MagicScriptTag = (props) => {
4948
const theme = parsedTheme[colorMode]
5049
iterate(theme)
5150
root.style.setProperty('--initial-color-mode', colorMode);
51+
52+
// FIX: Setting data-theme is required for global CSS styles
53+
root.setAttribute('data-theme', colorMode);
54+
55+
// Sync the calculated theme globally
5256
window.__theme = colorMode;
5357
})()
5458
`;
5559
return <script dangerouslySetInnerHTML={{ __html: codeToRunOnClient }} />;
5660
};
5761

58-
export const onRenderBody = ( { setPreBodyComponents }) => {
59-
setPreBodyComponents(<MagicScriptTag key="theme-injection" theme={themes} />);
60-
};
62+
// FIX: Using setHeadComponents instead of setPreBodyComponents
63+
export const onRenderBody = ( { setHeadComponents }) => {
64+
setHeadComponents([<MagicScriptTag key="theme-injection" theme={themes} />]);
65+
};

src/theme/app/StyledThemeProvider.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ export const StyledThemeProvider = (props) => {
1212
const { children, darkTheme, lightTheme } = props;
1313
const { isDark, didLoad } = useContext(ThemeManagerContext);
1414

15+
// For SSR, we need to provide a consistent theme initially
16+
// This ensures the server and client render the same thing initially
1517
const currentTheme = isDark ? darkTheme : lightTheme;
16-
17-
// Fallback to SSR-injected CSS variables during hydration to prevent FOUC.
1818
const theme = {
1919
...(didLoad ? currentTheme : transformTheme(currentTheme)),
2020
};
@@ -26,7 +26,6 @@ export const StyledThemeProvider = (props) => {
2626
);
2727
};
2828

29-
// Maps JS theme object to raw CSS variables (--key) initialized via onRenderBody.
3029
const transformTheme = (theme) => {
3130
const newTheme = {};
3231
Object.keys(theme).forEach((key) => {

src/theme/app/ThemeManager.js

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
// majority of code from https://www.joshwcomeau.com/react/dark-mode/ and https://github.com/gperl27/gatsby-styled-components-dark-mode
2-
// context provider for app to make accessible theme setting, toggle function, etc.
1+
//majority of code from https://www.joshwcomeau.com/react/dark-mode/ and https://github.com/gperl27/gatsby-styled-components-dark-mode
2+
3+
//context provider for app to make accessible theme setting, toggle function, etc.
34

45
import React, { createContext, useState, useEffect, useCallback } from "react";
56

@@ -36,16 +37,13 @@ const applyThemeToDOM = (theme) => {
3637
const root = window.document.documentElement;
3738
root.style.setProperty("--initial-color-mode", theme);
3839
root.setAttribute("data-theme", theme);
39-
40-
// Sync with SSR injected state
4140
window.__theme = theme;
4241
};
4342

4443
export const ThemeManagerProvider = (props) => {
4544
const [themeSetting, setThemeSetting] = useState(ThemeSetting.SYSTEM);
4645
const [didLoad, setDidLoad] = useState(false);
4746

48-
// Initialize state from SSR script to prevent hydration mismatch
4947
const [isDark, setIsDark] = useState(() => {
5048
if (isBrowser) {
5149
if (window.__theme === ThemeSetting.DARK) return true;
@@ -59,11 +57,8 @@ export const ThemeManagerProvider = (props) => {
5957

6058
const root = window.document.documentElement;
6159
const initialColorValue = (root.style.getPropertyValue("--initial-color-mode") || "").trim();
62-
63-
// Prioritize SSR-injected theme
6460
const actualTheme = window.__theme || initialColorValue || ThemeSetting.LIGHT;
6561

66-
// Get stored theme from localStorage
6762
const storedTheme = localStorage.getItem(DarkThemeKey);
6863

6964
if (storedTheme && storedTheme !== ThemeSetting.SYSTEM) {
@@ -85,7 +80,7 @@ export const ThemeManagerProvider = (props) => {
8580
setDidLoad(true);
8681
}, []);
8782

88-
// Listen to system color scheme changes only when on SYSTEM mode
83+
// Listen to system color scheme changes only when on SYSTEM mode
8984
useEffect(() => {
9085
if (!isBrowser || themeSetting !== ThemeSetting.SYSTEM) return;
9186

@@ -107,11 +102,11 @@ export const ThemeManagerProvider = (props) => {
107102
const newIsDark = !isDark;
108103
const newTheme = newIsDark ? ThemeSetting.DARK : ThemeSetting.LIGHT;
109104

110-
// Update state
105+
// Update state
111106
setIsDark(newIsDark);
112107
setThemeSetting(newTheme);
113108

114-
// Apply to DOM immediately
109+
// Apply to DOM immediately
115110
applyThemeToDOM(newTheme);
116111

117112
// Persist to localStorage
@@ -143,14 +138,14 @@ export const ThemeManagerProvider = (props) => {
143138
return;
144139
}
145140

146-
// Update state
141+
// Update state
147142
setIsDark(newIsDark);
148143
setThemeSetting(setting);
149144

150145
// Apply to DOM immediately
151146
applyThemeToDOM(themeToApply);
152147

153-
// Persist to localStorage
148+
// Persist to localStorage
154149
localStorage.setItem(DarkThemeKey, setting);
155150
},
156151
[isDark]

0 commit comments

Comments
 (0)