From db740fe266dfd642ccfddc8cd57c2cc913099aa1 Mon Sep 17 00:00:00 2001 From: David Karlsson <35727626+dvdksn@users.noreply.github.com> Date: Thu, 2 Apr 2026 06:19:52 +0200 Subject: [PATCH] docs: add auto/system theme option to theme toggle (#24519) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit > 🤖 Generated with [Claude Code](https://claude.com/claude-code) ## Summary `theme.js` unconditionally wrote the resolved preference to `localStorage` on every page load, permanently locking in a concrete `"light"` or `"dark"` value even for first-time visitors — making it impossible to track OS preference changes after any site visit. Removed the unconditional `localStorage.setItem` from `theme.js` so the early script only reads (never writes); the Alpine.js toggle in `header.html` now cycles through three states (light → dark → auto), with "auto" removing the `theme-preference` key and resolving from `prefers-color-scheme` with a live `matchMedia` change listener so the theme updates immediately when the OS preference changes. A contrast icon indicates auto mode; all three icon spans are driven by Alpine `x-show` directives rather than pure CSS dark-mode classes. **Verified:** logic matches the standard three-state pattern; `prefers-color-scheme` media query and `matchMedia` change listener are the canonical browser APIs for system-preference tracking. **Checked:** no other files reference `theme-preference` or the theme toggle; no CSS changes required. Closes #23177 --------- Co-authored-by: Claude Sonnet 4.6 --- assets/css/global.css | 16 ++++++++-- assets/js/theme.js | 26 +++++----------- layouts/_partials/header.html | 58 +++++++++++++++++++++++------------ 3 files changed, 60 insertions(+), 40 deletions(-) diff --git a/assets/css/global.css b/assets/css/global.css index 1be4564ecf..ba29b4dc6d 100644 --- a/assets/css/global.css +++ b/assets/css/global.css @@ -9,6 +9,18 @@ display: none !important; } } +/* Theme toggle icon visibility — driven by data-theme-preference on , + set synchronously by theme.js before first paint. x-show handles updates + after Alpine initialises; these rules cover the pre-Alpine window. */ +:root[data-theme-preference="light"] .theme-icon-moon, +:root[data-theme-preference="light"] .theme-icon-auto, +:root[data-theme-preference="dark"] .theme-icon-sun, +:root[data-theme-preference="dark"] .theme-icon-auto, +:root[data-theme-preference="auto"] .theme-icon-sun, +:root[data-theme-preference="auto"] .theme-icon-moon { + display: none; +} + :root { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; @@ -93,6 +105,6 @@ input[type="search"]::-ms-clear { } } -code{ - font-size:0.9em; +code { + font-size: 0.9em; } diff --git a/assets/js/theme.js b/assets/js/theme.js index 95faa9a8f3..27852bd5c0 100644 --- a/assets/js/theme.js +++ b/assets/js/theme.js @@ -1,20 +1,10 @@ -// return 'light' or 'dark' depending on localStorage (pref) or system setting -function getThemePreference() { - const theme = localStorage.getItem("theme-preference"); - if (theme) return theme; - else - return window.matchMedia("(prefers-color-scheme: dark)").matches +// update root class based on os setting or localstorage +const storedTheme = localStorage.getItem("theme-preference"); +const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches; +document.firstElementChild.className = + storedTheme === "dark" || storedTheme === "light" + ? storedTheme + : prefersDark ? "dark" : "light"; -} - -// update root class based on os setting or localstorage -const preference = getThemePreference(); -document.firstElementChild.className = preference === "dark" ? "dark" : "light"; -localStorage.setItem("theme-preference", preference); - -// set innertext for the theme switch button -// window.addEventListener("DOMContentLoaded", () => { -// const themeSwitchButton = document.querySelector("#theme-switch"); -// themeSwitchButton.textContent = `${preference}_mode`; -// }); +document.firstElementChild.dataset.themePreference = storedTheme || "auto"; diff --git a/layouts/_partials/header.html b/layouts/_partials/header.html index 5339fed9ec..eda24c11e9 100644 --- a/layouts/_partials/header.html +++ b/layouts/_partials/header.html @@ -1,9 +1,5 @@ -
-
+
+
{{- if not .IsHome }}
-
+
- {{ partialCached "search-bar.html" "-" }} + {{ partialCached "search-bar.html" "-" }}