docs: add auto/system theme option to theme toggle (#24519)

> 🤖 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 <noreply@anthropic.com>
This commit is contained in:
David KarlssonandClaude Sonnet 4.6 authored and GitHub committed 2026-04-02 06:19:52 +02:00
1 parent 850c008d85
commit db740fe266
3 files changed
+60 -40

No files matched your search

+14 -2
View File
@@ -9,6 +9,18 @@
display: none !important;
}
}
/* Theme toggle icon visibility — driven by data-theme-preference on <html>,
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;
}
+8 -18
View File
@@ -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";