From 501c20b6806546c87e5c0357ba45819fb2040c70 Mon Sep 17 00:00:00 2001 From: Tushar <80577646+TusharThakur04@users.noreply.github.com> Date: Sat, 18 Apr 2026 19:57:01 +0530 Subject: [PATCH] fix: resolve flash of unstyled content (FOUC) in dark mode --- src/generators/web/template.html | 2 +- src/generators/web/ui/theme-script.mjs | 25 +++++++++++++++++++++++++ src/generators/web/utils/processing.mjs | 2 ++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 src/generators/web/ui/theme-script.mjs diff --git a/src/generators/web/template.html b/src/generators/web/template.html index fe6a5823..397c4ae1 100644 --- a/src/generators/web/template.html +++ b/src/generators/web/template.html @@ -19,7 +19,7 @@ href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono&family=Open+Sans:ital,wght@0,300..800;1,300..800" /> - + diff --git a/src/generators/web/ui/theme-script.mjs b/src/generators/web/ui/theme-script.mjs new file mode 100644 index 00000000..8feea984 --- /dev/null +++ b/src/generators/web/ui/theme-script.mjs @@ -0,0 +1,25 @@ +'use strict'; + +/** + * This script is designed to be inlined in the of the HTML template. + * It must execute BEFORE the body renders to prevent a Flash of Unstyled Content (FOUC). + */ + +export default `(function initializeTheme() { + const THEME_STORAGE_KEY = 'theme'; + const THEME_DATA_ATTRIBUTE = 'data-theme'; + const DARK_QUERY = '(prefers-color-scheme: dark)'; + + const savedUserPreference = localStorage.getItem(THEME_STORAGE_KEY); + const systemSupportsDarkMode = window.matchMedia(DARK_QUERY).matches; + + const shouldApplyDark = + savedUserPreference === 'dark' || + (savedUserPreference === 'system' && systemSupportsDarkMode) || + (!savedUserPreference && systemSupportsDarkMode); + + const themeToApply = shouldApplyDark ? 'dark' : 'light'; + + document.documentElement.setAttribute(THEME_DATA_ATTRIBUTE, themeToApply); + document.documentElement.style.colorScheme = themeToApply; +})();`.trim(); diff --git a/src/generators/web/utils/processing.mjs b/src/generators/web/utils/processing.mjs index e4a66f23..7a2b1f19 100644 --- a/src/generators/web/utils/processing.mjs +++ b/src/generators/web/utils/processing.mjs @@ -13,6 +13,7 @@ import getConfig from '../../../utils/configuration/index.mjs'; import { populate } from '../../../utils/configuration/templates.mjs'; import { minifyHTML } from '../../../utils/html-minifier.mjs'; import { SPECULATION_RULES } from '../constants.mjs'; +import themeScript from '../ui/theme-script.mjs'; /** * Populates a template string by evaluating it as a JavaScript template literal, @@ -137,6 +138,7 @@ export async function processJSXEntries(entries, template) { importMap: clientBundle.importMap?.replaceAll('/', root) ?? '', entrypoint: `${data.api}.js?${randomUUID()}`, speculationRules: SPECULATION_RULES, + themeScript, root, metadata: data, config,