Skip to content

Commit 4fb6f67

Browse files
Merge pull request typst-doc-cn#14 from YDX-2147483647/dark
fix: screen should not flash in the dark mode
2 parents 591bd7c + 8915c67 commit 4fb6f67

File tree

5 files changed

+79
-35
lines changed

5 files changed

+79
-35
lines changed

index.typ

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
#show: load-html-template.with(
88
"/typ/templates/template.html",
99
extra-head: {
10-
vite.load-files(("src/main.ts",))
10+
vite.load-files(("src/main.ts", "src/theme.ts#nomodule"))
1111
},
1212
)
1313

src/main.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,4 @@ import "./global.css";
22
import "./show-example.css";
33
import "./util.css";
44

5-
import "./theme.ts";
6-
75
import "./respec/mod.ts";

src/theme.ts

Lines changed: 53 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,68 @@
1-
type Theme = 'auto' | 'light' | 'dark';
1+
/**
2+
* Theme toggle.
3+
*
4+
* This script should be executed before DOM is loaded.
5+
* Otherwise, the screen will flash in the dark mode.
6+
*/
27

3-
const themeToggle = document.getElementById("theme-toggle") as HTMLElement;
4-
const themeSelections: HTMLDivElement[] = Array.from(themeToggle.querySelectorAll('div.theme-icon'));
8+
type Theme = "auto" | "light" | "dark";
59

6-
// Load saved theme preference or default to auto (sync system)
7-
if (typeof localStorage !== "undefined") {
8-
const theme = localStorage.getItem("theme") ?? 'auto';
9-
applyTheme(theme as Theme);
10-
}
11-
12-
// Theme toggle functionality
13-
themeToggle.addEventListener("click", () => {
14-
const current = themeSelections.findIndex(icon => !icon.hidden);
15-
const next = themeSelections[(current + 1) % themeSelections.length].classList;
16-
const theme = next.contains("auto") ? "auto" : next.contains("light") ? "light" : "dark";
17-
applyTheme(theme);
18-
});
19-
20-
function applyTheme(theme: Theme): void {
10+
/** Same as `applyTheme`, but works before DOM is loaded */
11+
function applyThemeWithoutDOM(theme: Theme): void {
2112
// Change the page’s theme
22-
const resolved = theme === 'auto' ? getSystemTheme() : theme;
13+
const resolved = theme === "auto" ? getSystemTheme() : theme;
2314
if (resolved === "dark") {
2415
document.documentElement.classList.add("dark");
2516
} else {
2617
document.documentElement.classList.remove("dark");
2718
}
19+
}
2820

29-
// Update theme selections
30-
themeSelections.forEach(icon => {
31-
icon.hidden = !icon.classList.contains(theme);
21+
// Load saved theme preference or default to auto (sync system)
22+
if (typeof localStorage !== "undefined") {
23+
const theme = localStorage.getItem("theme") ?? "auto";
24+
applyThemeWithoutDOM(theme as Theme);
25+
}
26+
27+
document.addEventListener("DOMContentLoaded", () => {
28+
const themeToggle = document.getElementById("theme-toggle") as HTMLElement;
29+
const themeSelections: HTMLDivElement[] = Array.from(
30+
themeToggle.querySelectorAll("div.theme-icon"),
31+
);
32+
33+
// Load saved theme preference or default to auto (sync system)
34+
if (typeof localStorage !== "undefined") {
35+
const theme = localStorage.getItem("theme") ?? "auto";
36+
applyTheme(theme as Theme);
37+
}
38+
39+
// Theme toggle functionality
40+
themeToggle.addEventListener("click", () => {
41+
const current = themeSelections.findIndex((icon) => !icon.hidden);
42+
const next =
43+
themeSelections[(current + 1) % themeSelections.length].classList;
44+
const theme = next.contains("auto")
45+
? "auto"
46+
: next.contains("light")
47+
? "light"
48+
: "dark";
49+
applyTheme(theme);
3250
});
3351

34-
// Save for future loads
35-
localStorage.setItem("theme", theme);
36-
}
52+
function applyTheme(theme: Theme): void {
53+
applyThemeWithoutDOM(theme);
54+
55+
// Update theme selections
56+
themeSelections.forEach((icon) => {
57+
icon.hidden = !icon.classList.contains(theme);
58+
});
59+
60+
// Save for future loads
61+
localStorage.setItem("theme", theme);
62+
}
63+
});
3764

38-
function getSystemTheme(): 'dark' | 'light' {
65+
function getSystemTheme(): "dark" | "light" {
3966
const match = window.matchMedia &&
4067
window.matchMedia("(prefers-color-scheme: dark)").matches;
4168
return match ? "dark" : "light";

typ/packages/vite.typ

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,46 @@
66

77
#let manifest-path = "/dist/.vite/manifest.json"
88

9+
/// - raw-path (str):
10+
/// -> (path: str, as-module: bool)
11+
#let _parse-path(raw-path) = {
12+
if raw-path.ends-with("#nomodule") {
13+
(path: raw-path.trim("#nomodule", at: end), as-module: false)
14+
} else {
15+
(path: raw-path, as-module: true)
16+
}
17+
}
18+
919
/// Load a file
1020
///
11-
/// - paths (array<str>):
12-
/// - mode ("dev" | "prod"):
21+
/// All scripts will be loaded as a module be default.
22+
///
23+
/// If a script should be executed before DOM is loaded, add a `#nomodule` suffix.
24+
/// Note that it is not officially supported by vite, and does not work in `dev` mode.
25+
///
26+
/// - paths (array<str>): Path to typescript entry points.
1327
/// -> content
1428
#let load-files(paths) = {
1529
let url(path) = asset-url("/" + path)
1630

1731
if mode == "dev" {
1832
h.script({ }, type: "module", src: url("@vite/client"))
19-
for path in paths {
33+
for raw-path in paths {
34+
let (path, as-module) = _parse-path(raw-path)
2035
h.script({ }, type: "module", src: url(path))
2136
}
2237
} else {
2338
let manifest = json(manifest-path)
2439

25-
for chunk in manifest.values() {
40+
for raw-path in paths {
41+
let (path, as-module) = _parse-path(raw-path)
42+
let chunk = manifest.at(path)
43+
2644
assert("imports" not in chunk, message: "the `imports` field is not supported yet")
2745

28-
h.script({ }, type: "module", src: url(chunk.file))
46+
h.script({ }, src: url(chunk.file), ..if as-module { (type: "module") })
2947

30-
for css in chunk.css {
48+
for css in chunk.at("css", default: ()) {
3149
h.link({ }, rel: "stylesheet", href: url(css))
3250
}
3351
}

vite.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export default defineConfig({
1111
rollupOptions: {
1212
input: {
1313
main: resolve(__dirname, "src/main.ts"),
14+
theme: resolve(__dirname, "src/theme.ts"),
1415
},
1516
},
1617
manifest: true, // https://vite.dev/guide/backend-integration.html

0 commit comments

Comments
 (0)