From 600d42223cacd4163bdbc75900f1735c890a58ee Mon Sep 17 00:00:00 2001 From: Tanner Linsley Date: Thu, 23 Apr 2026 10:27:51 -0400 Subject: [PATCH] fix(theme): disable animations during theme switch for instant transition --- src/components/ThemeProvider.tsx | 11 +++++++++++ src/styles/app.css | 9 +++++++++ 2 files changed, 20 insertions(+) diff --git a/src/components/ThemeProvider.tsx b/src/components/ThemeProvider.tsx index ce6ba4e58..893a526e2 100644 --- a/src/components/ThemeProvider.tsx +++ b/src/components/ThemeProvider.tsx @@ -42,6 +42,8 @@ const getSystemTheme = createIsomorphicFn() const updateThemeClass = createClientOnlyFn((themeMode: ThemeMode) => { const root = document.documentElement + root.classList.add('theme-switching') + root.classList.remove('light', 'dark', 'auto') const newTheme = themeMode === 'auto' ? getSystemTheme() : themeMode root.classList.add(newTheme) @@ -57,6 +59,15 @@ const updateThemeClass = createClientOnlyFn((themeMode: ThemeMode) => { newTheme === 'dark' ? THEME_COLORS.dark : THEME_COLORS.light, ) } + + // Force reflow so the no-transition styles apply to the theme change, + // then remove the class on the next frame so subsequent interactions animate. + void root.offsetHeight + requestAnimationFrame(() => { + requestAnimationFrame(() => { + root.classList.remove('theme-switching') + }) + }) }) const getNextTheme = createClientOnlyFn((current: ThemeMode): ThemeMode => { diff --git a/src/styles/app.css b/src/styles/app.css index 778cb8796..99090e7f7 100644 --- a/src/styles/app.css +++ b/src/styles/app.css @@ -7,6 +7,15 @@ @custom-variant auto (&:is(.auto, .auto *)); @custom-variant aria-current (&[aria-current="location"]); +html.theme-switching, +html.theme-switching *, +html.theme-switching *::before, +html.theme-switching *::after { + transition: none !important; + animation-duration: 0s !important; + animation-delay: 0s !important; +} + @theme { /* Breakpoints */ --breakpoint-xs: 480px;