Skip to content

Commit 16bdc1d

Browse files
committed
Expose a script for SPA that can be loaded early to make sure to avoid white flashes
1 parent 1a24497 commit 16bdc1d

3 files changed

Lines changed: 105 additions & 3 deletions

File tree

scripts/build/build.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ import yargsParser from "yargs-parser";
106106
});
107107

108108
fs.cpSync(pathJoin(__dirname, "main.css"), pathJoin(distDirPath, "main.css"));
109+
fs.cpSync(
110+
pathJoin(__dirname, "early-color-scheme.js"),
111+
pathJoin(distDirPath, "early-color-scheme.js")
112+
);
109113

110114
fs.cpSync(pathJoin(dsfrDirPath, "favicon"), pathJoin(distDirPath, "favicon"), {
111115
"recursive": true
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/* eslint-disable no-undef */
2+
3+
{
4+
const isDark = (() => {
5+
query_param: {
6+
const value = new URLSearchParams(location.search).get("dark");
7+
8+
switch (value) {
9+
case "true":
10+
return true;
11+
case "false":
12+
return false;
13+
default:
14+
break query_param;
15+
}
16+
}
17+
18+
local_storage: {
19+
const colorSchemeReadFromLocalStorage = localStorage.getItem("scheme");
20+
21+
if (colorSchemeReadFromLocalStorage === null) {
22+
break local_storage;
23+
}
24+
25+
if (colorSchemeReadFromLocalStorage === "system") {
26+
break local_storage;
27+
}
28+
29+
switch (colorSchemeReadFromLocalStorage) {
30+
case "dark":
31+
return true;
32+
case "light":
33+
return false;
34+
default:
35+
break local_storage;
36+
}
37+
}
38+
39+
return matchMedia("(prefers-color-scheme: dark)").matches;
40+
})();
41+
42+
document.documentElement.setAttribute(
43+
"data-fr-scheme",
44+
(() => {
45+
const colorSchemeReadFromLocalStorage = localStorage.getItem("scheme");
46+
47+
if (colorSchemeReadFromLocalStorage === null) {
48+
return "system";
49+
}
50+
51+
return colorSchemeReadFromLocalStorage;
52+
})()
53+
);
54+
document.documentElement.setAttribute("data-fr-theme", isDark ? "dark" : "light");
55+
56+
{
57+
let element = document.getElementById("dsfr-root-color-scheme");
58+
59+
if (element !== null) {
60+
element.remove();
61+
}
62+
63+
element = document.createElement("style");
64+
65+
element.id = "dsfr-root-color-scheme";
66+
67+
element.innerHTML = `:root { color-scheme: ${isDark ? "dark" : "light"}; }`;
68+
69+
document.head.appendChild(element);
70+
}
71+
72+
{
73+
const name = "theme-color";
74+
75+
let element = document.querySelector(`meta[name=${name}]`);
76+
77+
if (element !== null) {
78+
element.remove();
79+
}
80+
81+
element = document.createElement("meta");
82+
83+
element.name = name;
84+
85+
element.content = isDark ? "#161616" : "#ffffff";
86+
87+
document.head.appendChild(element);
88+
}
89+
}

src/useIsDark/client.ts

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@ export type UseIsDark = () => {
3434
const $isAfterFirstEffect = createStatefulObservable(() => false);
3535

3636
export function getIsDarkClientSide() {
37-
return $isAfterFirstEffect.current ? $clientSideIsDark.current : ssrWasPerformedWithIsDark;
37+
if ($isAfterFirstEffect.current) {
38+
return $clientSideIsDark.current;
39+
}
40+
assert(ssrWasPerformedWithIsDark !== undefined);
41+
return ssrWasPerformedWithIsDark;
3842
}
3943

4044
function getSystemColorScheme() {
@@ -56,7 +60,7 @@ export const useIsDarkClientSide: UseIsDark = () => {
5660

5761
const isDark =
5862
isFirstRenderingOfTheComponent || !$isAfterFirstEffect.current
59-
? ssrWasPerformedWithIsDark
63+
? (assert(ssrWasPerformedWithIsDark !== undefined), ssrWasPerformedWithIsDark)
6064
: $clientSideIsDark.current;
6165

6266
const setIsDark = useConstCallback<ReturnType<UseIsDark>["setIsDark"]>(
@@ -97,7 +101,7 @@ export const useIsDarkClientSide: UseIsDark = () => {
97101
};
98102
};
99103

100-
let ssrWasPerformedWithIsDark: boolean;
104+
let ssrWasPerformedWithIsDark: boolean | undefined = undefined;
101105

102106
function getCurrentIsDarkFromHtmlAttribute(): boolean | undefined {
103107
const colorSchemeFromHtmlAttribute = document.documentElement.getAttribute(data_fr_theme);
@@ -114,6 +118,11 @@ function getCurrentIsDarkFromHtmlAttribute(): boolean | undefined {
114118
assert(false, `Unrecognized ${data_fr_theme} attribute value: ${colorSchemeFromHtmlAttribute}`);
115119
}
116120

121+
// Warning: There is a ton of implicit coupling here,
122+
// scripts/build/early-color-scheme.js
123+
// and
124+
// src/useIsDark/scriptToRunAsap.ts
125+
// need to be updated if anything is changed here.
117126
export function startClientSideIsDarkLogic(params: {
118127
registerEffectAction: (action: () => void) => void;
119128
doPersistDarkModePreferenceWithCookie: boolean;

0 commit comments

Comments
 (0)