From e03638fc2e0969e0c739f3703c84c006993d32a6 Mon Sep 17 00:00:00 2001 From: wuyangfan <1102042793@qq.com> Date: Sun, 17 May 2026 16:21:21 +0800 Subject: [PATCH] fix(theme): sync theme across tabs and clear stale classes Listen for localStorage `storage` events so other windows/tabs apply theme changes. When applying a theme, remove every known theme class from instead of only the previous one, avoiding multiple theme classes stacking after cross-window updates. Fixes #2618 Co-authored-by: Cursor --- crates/mdbook-html/front-end/js/book.js | 31 +++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/crates/mdbook-html/front-end/js/book.js b/crates/mdbook-html/front-end/js/book.js index fe74b7106f..0010bc27a4 100644 --- a/crates/mdbook-html/front-end/js/book.js +++ b/crates/mdbook-html/front-end/js/book.js @@ -388,6 +388,20 @@ aria-label="Show hidden lines">'; } let previousTheme = default_theme; + + function clear_theme_classes() { + themeIds.forEach(function(id) { + html.classList.remove(id.replace(/^mdbook-theme-/, '')); + }); + html.classList.remove(default_theme); + if (typeof default_light_theme !== 'undefined') { + html.classList.remove(default_light_theme); + } + if (typeof default_dark_theme !== 'undefined') { + html.classList.remove(default_dark_theme); + } + } + function set_theme(theme, store = true) { let ace_theme; @@ -427,12 +441,23 @@ aria-label="Show hidden lines">'; } } - html.classList.remove(previousTheme); + clear_theme_classes(); html.classList.add(theme); previousTheme = theme; updateThemeSelected(); } + window.addEventListener('storage', function(e) { + if (e.key !== 'mdbook-theme') { + return; + } + if (e.newValue === null) { + set_theme(get_theme(), false); + } else { + set_theme(e.newValue, false); + } + }); + const query = window.matchMedia('(prefers-color-scheme: dark)'); query.onchange = function() { set_theme(get_theme(), false); @@ -551,14 +576,16 @@ aria-label="Show hidden lines">'; } }); sidebarToggleButton.addEventListener('click', () => { + sidebarCheckbox.checked = !sidebarCheckbox.checked; /* To allow the sidebar expansion animation, we first need to put back the display. */ - if (!sidebarCheckbox.checked) { + if (sidebarCheckbox.checked) { sidebar.style.display = ''; // Workaround for Safari skipping the animation when changing // `display` and a transform in the same event loop. This forces a // reflow after updating the display. sidebar.offsetHeight; } + sidebarCheckbox.dispatchEvent(new Event('change', { bubbles: true })); }); function showSidebar() {