Suppress delayed color transition when switching theme (WEB-1207)
Version: 0.8.0 ยท Type: ๐ Bug Fix
Problemโ
Tree item's base style carries transition-colors โ intended to fade the hover / selected background in and out. But light / dark theme switching flips the entire set of CSS variables at once (the dark values are bound to both the .dark and [data-theme='dark'] selectors). A transitioned element cannot tell whether a color change comes from :hover or from a theme switch, so the moment the variables flip, transition-colors interpolates the old โ new colors over 150ms. The result is that Tree row background / border colors animate slowly during lightโdark toggles โ the perceived "delayed color switch". Because the title text / icon colors carry no transition (they switch instantly), the row also looks out of sync.
There is a second layer to the problem: the theme is toggled from more than one place. DesignProvider sets data-theme + toggles .dark itself, but design-site (Docusaurus) uses Docusaurus's own toggle (synchronously changing data-theme) and only mirrors .dark afterwards via a MutationObserver in Root.tsx โ it never goes through DesignProvider. Neither design-site nor web4 imports @plaud/design's globals.css. So the fix has to be a shared, self-contained utility that each toggle point can opt into, rather than something buried inside DesignProvider.
Changed Filesโ
packages/design/src/utils/theme-transition.ts(new)packages/design/src/utils/__tests__/theme-transition.test.ts(new)packages/design/src/utils/index.ts/packages/design/src/index.ts(export)packages/design/src/provider/DesignProvider.tsxpackages/design-site/src/theme/Root.tsx
Changesโ
- Add and export a shared
suppressThemeTransition(apply)utility. On first use it self-injects a one-off<style>(.pd-theme-switching * { transition: none !important; }, idempotent, one global copy) so it does not depend on the consumer importingglobals.cssโ apps in the monorepo (web4 / design-site) each have their own CSS entry, so only a self-contained util can work everywhere. suppressThemeTransitionadds.pd-theme-switchingto<html>before runningapply(the callback that actually flips the theme DOM state), forces one synchronous style recalc (getComputedStyle) so the variable flip commits while transitions are disabled, then removes the class on the nextrequestAnimationFrame. Theme switching becomes instant while hover / selected transitions are unaffected.DesignProvider's themeuseEffectnow wraps itsdata-theme/.darkflip insuppressThemeTransition.- design-site's
Root.tsxMutationObserverwraps its.darksync insuppressThemeTransition(syncDarkClass)when it detects adata-themechange โ the observer callback still runs before the browser paints, so it is in time to commit the variable flip with transitions disabled. - Add unit tests for the utility (class lifecycle across the frame, single
applyinvocation, idempotent style injection).