fix(web): sync data-seed-user-color-scheme so system dark-mode follows OS

This commit is contained in:
Munkherdene 2026-08-22 23:25:10 +08:00
parent 6260cc3479
commit 2e391ce244
2 changed files with 29 additions and 1 deletions

View file

@ -12,7 +12,14 @@ const THEME_INIT_SCRIPT = `
try {
var v = localStorage.getItem("mercury.theme");
var mode = v === "light" ? "light-only" : v === "dark" ? "dark-only" : "system";
document.documentElement.setAttribute("data-seed-color-mode", mode);
var el = document.documentElement;
el.setAttribute("data-seed-color-mode", mode);
// Seed's "system" mode resolves dark ONLY when data-seed-user-color-scheme
// reflects the OS preference — the CSS has no prefers-color-scheme media
// query of its own. Sync it from matchMedia so "system" actually follows
// the OS (light-only/dark-only ignore this attribute).
var dark = window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches;
el.setAttribute("data-seed-user-color-scheme", dark ? "dark" : "light");
} catch (e) {}
})();
`;

View file

@ -39,9 +39,19 @@ function persistTheme(theme: MercuryTheme): void {
}
}
/** Seed's "system" color-mode resolves dark only via data-seed-user-color-scheme
* (there's no prefers-color-scheme media query in the tokens), so we sync it
* from matchMedia. light-only/dark-only ignore this attribute. */
function syncUserScheme(): void {
if (typeof window === "undefined" || !window.matchMedia) return;
const dark = window.matchMedia("(prefers-color-scheme: dark)").matches;
document.documentElement.setAttribute("data-seed-user-color-scheme", dark ? "dark" : "light");
}
function applyTheme(theme: MercuryTheme): void {
if (typeof document === "undefined") return;
document.documentElement.setAttribute("data-seed-color-mode", toColorMode(theme));
syncUserScheme();
}
export interface ThemeToggleProps {
@ -60,6 +70,17 @@ export function ThemeToggle({ labels = DEFAULT_LABELS }: ThemeToggleProps) {
// DOM before this ever mounts, so both agree from the first paint.
const [theme, setTheme] = React.useState<MercuryTheme>(() => getStoredTheme());
// Keep data-seed-user-color-scheme synced to the OS so "system" mode follows
// live OS light/dark changes without a reload.
React.useEffect(() => {
if (typeof window === "undefined" || !window.matchMedia) return;
const mq = window.matchMedia("(prefers-color-scheme: dark)");
syncUserScheme();
const onChange = () => syncUserScheme();
mq.addEventListener?.("change", onChange);
return () => mq.removeEventListener?.("change", onChange);
}, []);
function handleChange(next: string) {
const nextTheme = next as MercuryTheme;
setTheme(nextTheme);