From b8c0e3f16f3ec550fad41cee2841628b7e2f1a5f Mon Sep 17 00:00:00 2001 From: David Karlsson <35727626+dvdksn@users.noreply.github.com> Date: Wed, 25 Mar 2026 11:00:46 +0100 Subject: [PATCH 1/6] docs: add auto/system theme option to theme toggle (fixes #23177) On every page load, theme.js was unconditionally writing the resolved preference to localStorage, permanently overriding the OS dark-mode setting after the first visit. Removed the unconditional write and dead commented-out code from theme.js; the script now only reads and applies the class. The header Alpine component now manages the full lifecycle with a three-state cycle (light -> dark -> auto): in auto mode it removes the localStorage key, resolves from prefers-color-scheme, and listens for live system preference changes via matchMedia. Co-Authored-By: Claude Sonnet 4.6 --- assets/js/theme.js | 25 +++++----------- layouts/_partials/header.html | 56 ++++++++++++++++++++++------------- 2 files changed, 43 insertions(+), 38 deletions(-) diff --git a/assets/js/theme.js b/assets/js/theme.js index 95faa9a8f3a..71228b0421a 100644 --- a/assets/js/theme.js +++ b/assets/js/theme.js @@ -1,20 +1,9 @@ -// 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`; -// }); diff --git a/layouts/_partials/header.html b/layouts/_partials/header.html index 5339fed9ec8..b74540cb1b1 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" "-" }}
From 1a5d9ed7a7214e483cc45de8c9645e2b8d87755e Mon Sep 17 00:00:00 2001 From: David Karlsson <35727626+dvdksn@users.noreply.github.com> Date: Wed, 25 Mar 2026 11:23:38 +0100 Subject: [PATCH 2/6] fix: address docker-agent review comments on theme toggle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Use icons/contrast.svg (not "contrast") to avoid build failure — the short name resolves to contrast-fill.svg which doesn't exist - Remove eager applyTheme(theme) call on init to prevent double-apply and potential flicker (theme.js already set the class on page load) - Store mql change listener in variable and return cleanup function from x-init so Alpine can remove it if the component is destroyed - Add hidden attribute to moon and contrast spans as no-JS fallback so only the sun icon shows when Alpine fails to initialise Co-Authored-By: Claude Sonnet 4.6 --- layouts/_partials/header.html | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/layouts/_partials/header.html b/layouts/_partials/header.html index b74540cb1b1..ed46e312493 100644 --- a/layouts/_partials/header.html +++ b/layouts/_partials/header.html @@ -93,20 +93,21 @@ document.firstElementChild.className = val; } } - applyTheme(theme); - mql.addEventListener('change', e => { if (theme === 'auto') document.firstElementChild.className = e.matches ? 'dark' : 'light'; }); + let handler = e => { if (theme === 'auto') document.firstElementChild.className = e.matches ? 'dark' : 'light'; }; + mql.addEventListener('change', handler); $watch('theme', val => applyTheme(val)); + return () => mql.removeEventListener('change', handler); " @click="theme = (theme === 'light' ? 'dark' : theme === 'dark' ? 'auto' : 'light')" > {{ partialCached "icon" "icons/sun.svg" "sun" }} - + - - {{ partialCached "icon" "contrast" "contrast" }} +
From 3773425f1ebe97b741e9d2ff9dd945bf668adcf9 Mon Sep 17 00:00:00 2001 From: David Karlsson <35727626+dvdksn@users.noreply.github.com> Date: Wed, 25 Mar 2026 11:30:13 +0100 Subject: [PATCH 3/6] revert: restore original contrast icon reference Reverts the icon path change from the previous commit. contrast-fill.svg does exist at node_modules/@material-symbols/svg-400/rounded/contrast-fill.svg and is correctly mounted to assets/icons/ by Hugo. The short name "contrast" resolves to icons/contrast-fill.svg as intended. The docker-agent review comment was incorrect. Co-Authored-By: Claude Sonnet 4.6 --- layouts/_partials/header.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/layouts/_partials/header.html b/layouts/_partials/header.html index ed46e312493..a4f1723bc7d 100644 --- a/layouts/_partials/header.html +++ b/layouts/_partials/header.html @@ -107,7 +107,7 @@ {{ partialCached "icon" "icons/moon.svg" "moon" }}
From f8e77cd5883dc3f2665400cfa5a0cbf1db7851ae Mon Sep 17 00:00:00 2001 From: David Karlsson <35727626+dvdksn@users.noreply.github.com> Date: Wed, 25 Mar 2026 11:34:05 +0100 Subject: [PATCH 4/6] fix: remove hidden attribute from moon and contrast icon spans Alpine x-show toggles inline style="display:none" but does not remove the HTML hidden attribute. With hidden present, the browser stylesheet's display:none from [hidden] persists even when x-show evaluates to true, so the moon and contrast icons never become visible. Co-Authored-By: Claude Sonnet 4.6 --- layouts/_partials/header.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/layouts/_partials/header.html b/layouts/_partials/header.html index a4f1723bc7d..8b3a6ec914d 100644 --- a/layouts/_partials/header.html +++ b/layouts/_partials/header.html @@ -103,10 +103,10 @@ {{ partialCached "icon" "icons/sun.svg" "sun" }} -