Animated Light/Dark Theme Switch

Use the View Transitions API for the now-iconic 'circle wipe' theme switch effect — a creative use of clip-path on the snapshot pseudo-elements.

UI Patterns

Detailed Explanation

The Circle-Wipe Effect

Made famous by Chrome's own DevRel demos, this effect grows a circular reveal from the click position, transitioning the entire page from light to dark mode through a clean wipe.

async function toggleTheme(event) {
  const x = event.clientX;
  const y = event.clientY;

  const transition = document.startViewTransition(() => {
    document.documentElement.classList.toggle('dark');
  });

  await transition.ready;

  const endRadius = Math.hypot(
    Math.max(x, innerWidth - x),
    Math.max(y, innerHeight - y),
  );

  document.documentElement.animate(
    {
      clipPath: [
        `circle(0px at ${x}px ${y}px)`,
        `circle(${endRadius}px at ${x}px ${y}px)`,
      ],
    },
    {
      duration: 500,
      easing: 'ease-in-out',
      pseudoElement: '::view-transition-new(root)',
    },
  );
}

How it works

  1. startViewTransition captures the light theme.
  2. The class toggle switches to dark; React re-renders.
  3. The browser captures the dark theme.
  4. await transition.ready waits until the snapshots are ready.
  5. We animate clip-path on the ::view-transition-new(root) pseudo-element — revealing the new (dark) snapshot through a growing circle while the old (light) snapshot stays visible underneath.

Direction reversal

For dark→light, animate the old pseudo-element shrinking instead of the new one growing. Or always animate the new one growing — it works equally well in both directions because the user perceives the change identically.

Reduced motion

if (matchMedia('(prefers-reduced-motion: reduce)').matches) {
  document.documentElement.classList.toggle('dark');
  return;
}

Skip the animation entirely for users with the preference set.

Use Case

Theme switchers in marketing sites and dashboards where the visual flourish is part of the brand. Production examples: Chrome 111+ DevRel demos, several CSS Day talks, and Linear's settings panel.

Try ItView Transitions API Generator

Open full tool