Skip to content

Commit b96e4e5

Browse files
fix: resolve flash of unstyled content in dark mode (#766)
1 parent 3fbb9fb commit b96e4e5

3 files changed

Lines changed: 29 additions & 1 deletion

File tree

src/generators/web/template.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono&family=Open+Sans:ital,wght@0,300..800;1,300..800" />
1818

1919
<!-- Apply theme before paint to avoid Flash of Unstyled Content -->
20-
<script>document.documentElement.setAttribute("data-theme", document.documentElement.style.colorScheme = localStorage.getItem("theme") || (matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"));</script>
20+
<script>${themeScript}</script>
2121
<script type="importmap">${importMap}</script>
2222
<script type="speculationrules">${speculationRules}</script>
2323
</head>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
'use strict';
2+
3+
/**
4+
* This script is designed to be inlined in the <head> of the HTML template.
5+
* It must execute BEFORE the body renders to prevent a Flash of Unstyled Content (FOUC).
6+
*/
7+
function initializeTheme() {
8+
var THEME_STORAGE_KEY = 'theme';
9+
var THEME_DATA_ATTRIBUTE = 'data-theme';
10+
var DARK_QUERY = '(prefers-color-scheme: dark)';
11+
12+
var savedUserPreference = localStorage.getItem(THEME_STORAGE_KEY);
13+
var systemSupportsDarkMode = window.matchMedia(DARK_QUERY).matches;
14+
15+
var shouldApplyDark =
16+
savedUserPreference === 'dark' ||
17+
(savedUserPreference === 'system' && systemSupportsDarkMode) ||
18+
(!savedUserPreference && systemSupportsDarkMode);
19+
20+
var themeToApply = shouldApplyDark ? 'dark' : 'light';
21+
22+
document.documentElement.setAttribute(THEME_DATA_ATTRIBUTE, themeToApply);
23+
document.documentElement.style.colorScheme = themeToApply;
24+
}
25+
26+
export var THEME_SCRIPT = `(${initializeTheme.toString()})();`;

src/generators/web/utils/processing.mjs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import getConfig from '../../../utils/configuration/index.mjs';
1313
import { populate } from '../../../utils/configuration/templates.mjs';
1414
import { minifyHTML } from '../../../utils/html-minifier.mjs';
1515
import { SPECULATION_RULES } from '../constants.mjs';
16+
import { THEME_SCRIPT } from '../ui/theme-script.mjs';
1617

1718
/**
1819
* Populates a template string by evaluating it as a JavaScript template literal,
@@ -195,6 +196,7 @@ export async function processJSXEntries(
195196
importMap: clientBundle.importMap?.replaceAll('/', root) ?? '',
196197
entrypoint: `${data.api}.js?${randomUUID()}`,
197198
speculationRules: SPECULATION_RULES,
199+
themeScript: THEME_SCRIPT,
198200
root,
199201
metadata: data,
200202
config,

0 commit comments

Comments
 (0)