Disable transitions on theme toggle

03/19/20

It's difficult to transition between themes smoothly. Adding a CSS transition to every element negatively impacts rendering performance, and it also won't work for images, icons, and CSS properties that don't support transitions.

Instead, we can temporarily remove transitions from all elements so that toggling themes feels snappy and consistent. We'll manually create a stylesheet that disables transitions:

const css = document.createElement('style')
css.type = 'text/css'
css.appendChild(
  document.createTextNode(
    `* {
       -webkit-transition: none !important;
       -moz-transition: none !important;
       -o-transition: none !important;
       -ms-transition: none !important;
       transition: none !important;
    }`
  )
)
document.head.appendChild(css)

Note that we need to manually specify browser prefixes, as this CSS isn't run through any preprocessing.

After changing the theme (usually this involves toggling a class on <body>), we force a browser repaint and remove the stylesheet:

// Toggle the theme here...

// Calling getComputedStyle forces the browser to redraw
const _ = window.getComputedStyle(css).opacity
document.head.removeChild(css)

Calling requestAnimationFrame before removing the stylesheet seemed to work at first, but it was unreliable and elements still transitioned. Using getComputedStyle works reliably on every major browser, because it forcibly applies all active stylesheets.

Before:

Toggling between light and dark theme, with elements flashing

After:

Toggling between light and dark theme, with no elements flashing

Thanks to Guillermo for the idea!