Modal Open and Close with View Transitions

Replace fragile portal-based animation libraries with native View Transitions for a smooth modal open/close that respects DOM order and accessibility.

UI Patterns

Detailed Explanation

The Modal Lifecycle in Three Phases

A polished modal animation needs to coordinate (1) the backdrop fading in, (2) the dialog scaling and fading in, and (3) the rest of the page subtly receding. The View Transitions API does (1) and (3) for free — you only need to author (2).

<dialog id="dlg" class="modal">…</dialog>
<button onclick="open()">Open</button>
<script>
  function open() {
    document.startViewTransition(() => {
      document.getElementById('dlg').showModal();
    });
  }
</script>
.modal {
  view-transition-name: app-modal;
}
@keyframes modal-out {
  to { transform: scale(0.96) translateY(8px); opacity: 0; }
}
@keyframes modal-in {
  from { transform: scale(0.96) translateY(8px); opacity: 0; }
}
::view-transition-old(app-modal) {
  animation: 180ms ease-in both modal-out;
}
::view-transition-new(app-modal) {
  animation: 240ms cubic-bezier(0.16, 1, 0.3, 1) both modal-in;
}

Why <dialog> plus View Transitions wins

The native <dialog> element handles focus trap, Esc to close, and scroll lock automatically. View Transitions handle the visual choreography. The combination replaces the entire React Modal / Headless UI overlay stack for most use cases — with about 12 lines of code instead of a dependency.

Closing animation

For close, call the same wrapper:

function close() {
  document.startViewTransition(() => {
    document.getElementById('dlg').close();
  });
}

The ::view-transition-old(app-modal) rule fires automatically because the dialog disappears in the snapshot.

Accessibility: prefers-reduced-motion

Always wrap your custom keyframes in a media query so users who request reduced motion still get an instant swap with zero animation:

@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(app-modal),
  ::view-transition-new(app-modal) {
    animation-duration: 0ms !important;
  }
}

Use Case

Replacing custom modal libraries (React Modal, Reach Dialog, Headless UI Dialog) when you want native animation, native accessibility (via &lt;dialog&gt;), and zero JavaScript overhead beyond a single startViewTransition call.

Try ItView Transitions API Generator

Open full tool