Skip to content

Radial Wipe

A copy-paste view transitions component in pure HTML, CSS & vanilla JS. Zero dependencies, framework-agnostic, MIT-licensed.

View TransitionsHTMLJavaScriptCSSany framework
Click to wipe

Copy into your project

HTML
<div class="nuda-vt2-radial-wipe">
  <div class="nuda-vt2-radial-wipe__stage" data-state="a" role="button" tabindex="0" aria-label="Swap panel with a radial wipe from the click point">
    <span class="nuda-vt2-radial-wipe__panel">Click to wipe</span>
  </div>
</div>
JavaScript
/* Radial Wipe — vanilla JS
   Captures the click position as a % within the stage, exposes it as CSS
   custom properties, then swaps state inside a view transition so the new
   snapshot reveals from that exact point. Keyboard users (Enter/Space) get a
   center-origin wipe since there is no pointer position to read. */

(function () {
  document.querySelectorAll(".nuda-vt2-radial-wipe__stage").forEach(function (stage) {
    var panel = stage.querySelector(".nuda-vt2-radial-wipe__panel");

    function doSwap() {
      var next = stage.dataset.state === "a" ? "b" : "a";
      stage.dataset.state = next;
      panel.textContent = next === "a" ? "Click to wipe" : "Wiped!";
    }

    function trigger() {
      if (!document.startViewTransition) { doSwap(); return; }
      document.startViewTransition(doSwap);
    }

    stage.addEventListener("click", function (e) {
      var rect = stage.getBoundingClientRect();
      var x = ((e.clientX - rect.left) / rect.width) * 100;
      var y = ((e.clientY - rect.top) / rect.height) * 100;
      document.documentElement.style.setProperty("--vt2-radial-x", x + "%");
      document.documentElement.style.setProperty("--vt2-radial-y", y + "%");
      trigger();
    });

    stage.addEventListener("keydown", function (e) {
      if (e.key !== "Enter" && e.key !== " ") return;
      e.preventDefault();
      document.documentElement.style.setProperty("--vt2-radial-x", "50%");
      document.documentElement.style.setProperty("--vt2-radial-y", "50%");
      trigger();
    });
  });
})();
CSS
.nuda-vt2-radial-wipe-demo {
  padding: .5rem;
}

.nuda-vt2-radial-wipe__stage {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 150px;
  height: 84px;
  border-radius: 12px;
  border: 1px solid rgba(255,255,255,.1);
  cursor: pointer;
}

.nuda-vt2-radial-wipe__stage[data-state="a"] {
  background: #18181b;
  view-transition-name: vt2-radial-wipe-stage;
}

.nuda-vt2-radial-wipe__stage[data-state="b"] {
  background: #1f2937;
  view-transition-name: vt2-radial-wipe-stage;
}

.nuda-vt2-radial-wipe__stage:focus-visible {
  outline: 2px solid #e4ff54;
  outline-offset: 2px;
}

.nuda-vt2-radial-wipe__panel {
  font-size: .72rem;
  font-weight: 700;
  color: #e4ff54;
  pointer-events: none;
}

@keyframes _nuda-vt2radialwipein {
  from {
    clip-path: circle(0% at var(--vt2-radial-x,50%) var(--vt2-radial-y,50%));
  }
  to {
    clip-path: circle(150% at var(--vt2-radial-x,50%) var(--vt2-radial-y,50%));
  }
}

::view-transition-old(vt2-radial-wipe-stage) {
  animation: none;
}

::view-transition-new(vt2-radial-wipe-stage) {
  animation: _nuda-vt2radialwipein .5s cubic-bezier(.4,0,.2,1) both;
}

@supports not (view-transition-name:none) {
  .nuda-vt2-radial-wipe__stage {
    transition: background-color .3s ease;
  }
}

@media (prefers-reduced-motion:reduce) {
  ::view-transition-new(vt2-radial-wipe-stage) {
    animation: none;
  }
  .nuda-vt2-radial-wipe__stage {
    transition: none;
  }
}

How to use Radial Wipe

Paste the HTML where you need it and the CSS into a global stylesheet (or a <style> tag). Every class is prefixed nuda- so it never collides with Tailwind or your own styles. Tweak the CSS custom properties to match your design system.

Works in React, Vue, Svelte, Astro, Next.js, Nuxt, Laravel Blade, Django, Rails — or a single .html file. No npm install, no build step.

More view transitions components

← Browse all NudaUI components