Skip to content

Commit 2c7c674

Browse files
committed
fix(theme): respect system color-scheme preference on initial page load
Fix by reading the theme config from a data-theme attribute on <html> (rendered by Zola), falling back to matchMedia for system preference, forcing syntax highlighting stylesheets to match, and only persisting to localStorage on explicit toggle clicks.
1 parent 32f1d24 commit 2c7c674

3 files changed

Lines changed: 38 additions & 27 deletions

File tree

static/js/init-theme.js

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,22 @@
77

88
(function () {
99
try {
10-
const storedTheme = localStorage.getItem("theme-storage");
11-
const defaultTheme = "{{ config.extra.theme | default(value='toggle') }}";
12-
let theme;
10+
var storedTheme = localStorage.getItem("theme-storage");
11+
var defaultTheme = document.documentElement.dataset.theme || "toggle";
12+
var theme;
1313

14-
if (["light", "dark", "auto"].includes(defaultTheme)) {
14+
if (defaultTheme === "light" || defaultTheme === "dark") {
1515
theme = defaultTheme;
16-
} else if (storedTheme) {
17-
theme = storedTheme;
16+
} else if (defaultTheme === "auto" || defaultTheme === "toggle") {
17+
if (storedTheme === "light" || storedTheme === "dark") {
18+
theme = storedTheme;
19+
} else {
20+
theme = window.matchMedia("(prefers-color-scheme: dark)").matches
21+
? "dark"
22+
: "light";
23+
}
1824
} else {
19-
theme = "dark"; // fallback default
25+
theme = "dark";
2026
}
2127

2228
// Apply theme class directly
@@ -25,14 +31,21 @@
2531
if (document.body) {
2632
document.body.classList.add(theme);
2733
} else {
28-
// Defer body class application
2934
window.addEventListener("DOMContentLoaded", function () {
3035
document.body.classList.add(theme);
3136
});
3237
}
38+
39+
// Force syntax highlighting stylesheets to match the resolved theme.
40+
// Without this, the browser applies them based on OS prefers-color-scheme,
41+
// which may not match the stored user preference.
42+
var d = document.getElementById("giallo-dark");
43+
var l = document.getElementById("giallo-light");
44+
if (d && l) {
45+
d.media = theme === "dark" ? "all" : "not all";
46+
l.media = theme === "light" ? "all" : "not all";
47+
}
3348
} catch (e) {
34-
// In case localStorage access fails
3549
document.documentElement.classList.add("dark");
36-
document.body.classList.add("dark");
3750
}
3851
})();

static/js/toggle-theme.js

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,18 @@ document.addEventListener("DOMContentLoaded", function () {
1010
const defaultThemeOption = document.documentElement.dataset.theme || "toggle";
1111
let currentTheme = storedTheme || defaultThemeOption;
1212

13-
// Prioritize `config.extra.theme` over localStorage, if available
14-
if (
15-
defaultThemeOption === "dark" ||
16-
defaultThemeOption === "light" ||
17-
defaultThemeOption === "auto"
18-
) {
13+
if (defaultThemeOption === "dark" || defaultThemeOption === "light") {
14+
// Fixed theme set in config
1915
currentTheme = defaultThemeOption;
20-
} else if (storedTheme) {
16+
} else if (storedTheme === "light" || storedTheme === "dark") {
17+
// User explicitly toggled before — respect their choice
2118
currentTheme = storedTheme;
2219
} else {
23-
// Set to prefer user system preference, if available, else default to dark
20+
// No explicit choice — follow system preference
2421
try {
25-
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
26-
.matches
22+
currentTheme = window.matchMedia("(prefers-color-scheme: dark)").matches
2723
? "dark"
2824
: "light";
29-
currentTheme = systemTheme;
3025
} catch (e) {
3126
currentTheme = "dark";
3227
}
@@ -53,11 +48,14 @@ document.addEventListener("DOMContentLoaded", function () {
5348
});
5449

5550
/**
56-
* Updates the theme mode in local storage and applies it to the page.
51+
* Updates the theme mode and applies it to the page.
5752
* @param {string} mode - The theme mode to set ("light" or "dark").
53+
* @param {boolean} [persist=false] - Whether to save the choice to localStorage.
5854
*/
59-
function setTheme(mode) {
60-
localStorage.setItem("theme-storage", mode);
55+
function setTheme(mode, persist) {
56+
if (persist) {
57+
localStorage.setItem("theme-storage", mode);
58+
}
6159
document.documentElement.classList.remove("light", "dark");
6260
document.body.classList.remove("light", "dark");
6361

@@ -105,7 +103,7 @@ function toggleTheme() {
105103
const newTheme = document.documentElement.classList.contains("dark")
106104
? "light"
107105
: "dark";
108-
setTheme(newTheme);
106+
setTheme(newTheme, true);
109107
}
110108

111109
/**

templates/_base.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
{% import "macros/index_macros.html" as index_macros %}
33

44
<!doctype html>
5-
<html lang="en">
5+
<html lang="en" data-theme="{{ config.extra.theme | default(value='toggle') }}">
66
<head>
77
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
88
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
@@ -143,7 +143,7 @@
143143
<!-- Load Fonts -->
144144
{% include "partials/fonts.html" %}
145145

146-
<!-- Pass Theme Preference as Data Attribute -->
146+
<!-- Initialize theme before paint -->
147147
<script src="{{ get_url(path='js/init-theme.js', trailing_slash=false) | safe }}"></script>
148148

149149
<!-- Additional scripts -->

0 commit comments

Comments
 (0)