From 876eb5459a182f12baea8373c7768f23b2ab2b00 Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Tue, 16 Dec 2025 08:59:35 +0100 Subject: [PATCH 1/3] Use View Transitions for smooth theme switching --- .../controllers/theme_controller.js | 24 +++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/app/javascript/controllers/theme_controller.js b/app/javascript/controllers/theme_controller.js index 154bf4fc0e..eba32a8c4e 100644 --- a/app/javascript/controllers/theme_controller.js +++ b/app/javascript/controllers/theme_controller.js @@ -26,13 +26,27 @@ export default class extends Controller { set #theme(theme) { localStorage.setItem("theme", theme) - if (theme === "auto") { - document.documentElement.removeAttribute("data-theme") - } else { - document.documentElement.setAttribute("data-theme", theme) + const currentTheme = document.documentElement.getAttribute("data-theme") || "auto" + const isInitialLoad = currentTheme === theme + + const prefersReducedMotion = window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches + const animate = !isInitialLoad && !prefersReducedMotion + + const applyTheme = () => { + if (theme === "auto") { + document.documentElement.removeAttribute("data-theme") + } else { + document.documentElement.setAttribute("data-theme", theme) + } + + this.#updateButtons() } - this.#updateButtons() + if (animate && document.startViewTransition) { + document.startViewTransition(applyTheme) + } else { + applyTheme() + } } #applyStoredTheme() { From 3216f1ca34dba28445c6a312253c5fbeeaa3c807 Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Tue, 16 Dec 2025 09:21:09 +0100 Subject: [PATCH 2/3] Fix `.nav__trigger` background flashing --- app/assets/stylesheets/nav.css | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/app/assets/stylesheets/nav.css b/app/assets/stylesheets/nav.css index 57d1803bd4..c48a780348 100644 --- a/app/assets/stylesheets/nav.css +++ b/app/assets/stylesheets/nav.css @@ -3,7 +3,7 @@ /* ------------------------------------------------------------------------ */ .nav__trigger { - --input-background: var(--color-canvas); + --input-background: transparent; --input-border-color: transparent; --input-padding: 0.3em 2em 0.3em 0.75em; @@ -17,21 +17,11 @@ @media (any-hover: hover) { &:hover { - filter: brightness(0.9); + --input-background: var(--color-ink-lighter); span { background: var(--color-ink-lighter); } - - html[data-theme="dark"] & { - --input-background: var(--color-ink-lighter); - } - - @media (prefers-color-scheme: dark) { - html:not([data-theme]) & { - --input-background: var(--color-ink-lighter); - } - } } } From 7cf0e578bc10a452a7b70a2e387fd6daab79b7c8 Mon Sep 17 00:00:00 2001 From: Alexander Zaytsev Date: Tue, 16 Dec 2025 15:14:15 +0100 Subject: [PATCH 3/3] Rename `isInitialLoad` to `hasChanged` --- app/javascript/controllers/theme_controller.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/controllers/theme_controller.js b/app/javascript/controllers/theme_controller.js index eba32a8c4e..cf64df5794 100644 --- a/app/javascript/controllers/theme_controller.js +++ b/app/javascript/controllers/theme_controller.js @@ -27,10 +27,10 @@ export default class extends Controller { localStorage.setItem("theme", theme) const currentTheme = document.documentElement.getAttribute("data-theme") || "auto" - const isInitialLoad = currentTheme === theme + const hasChanged = currentTheme !== theme const prefersReducedMotion = window.matchMedia?.("(prefers-reduced-motion: reduce)")?.matches - const animate = !isInitialLoad && !prefersReducedMotion + const animate = hasChanged && !prefersReducedMotion const applyTheme = () => { if (theme === "auto") {