Skip to main content

Suppress delayed color transition when switching theme (WEB-1207)

Version: 0.8.0 ยท Type: ๐Ÿ› Bug Fix

WEB-1207

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.tsx
  • packages/design-site/src/theme/Root.tsx

Changesโ€‹

  1. 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 importing globals.css โ€” apps in the monorepo (web4 / design-site) each have their own CSS entry, so only a self-contained util can work everywhere.
  2. suppressThemeTransition adds .pd-theme-switching to <html> before running apply (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 next requestAnimationFrame. Theme switching becomes instant while hover / selected transitions are unaffected.
  3. DesignProvider's theme useEffect now wraps its data-theme / .dark flip in suppressThemeTransition.
  4. design-site's Root.tsx MutationObserver wraps its .dark sync in suppressThemeTransition(syncDarkClass) when it detects a data-theme change โ€” the observer callback still runs before the browser paints, so it is in time to commit the variable flip with transitions disabled.
  5. Add unit tests for the utility (class lifecycle across the frame, single apply invocation, idempotent style injection).