Skip to content

Grid to Detail

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

View TransitionsHTMLCSSJavaScriptany framework

Copy into your project

HTML
<div class="nuda-vt-grid">
  <div class="nuda-vt-grid__stage" data-view="grid">
    <div class="nuda-vt-grid__grid">
      <span class="nuda-vt-grid__thumb"></span>
      <span class="nuda-vt-grid__thumb nuda-vt-grid__thumb--hero"></span>
      <span class="nuda-vt-grid__thumb"></span>
      <span class="nuda-vt-grid__thumb"></span>
    </div>
    <div class="nuda-vt-grid__hero"></div>
  </div>
  <button type="button" class="nuda-vt-grid__btn">Open / close</button>
</div>
CSS
/* Grid to Detail
   A gallery thumbnail expands into a hero. The thumbnail and the hero share one
   view-transition-name, so the API morphs the small tile into the large view
   (and back) — the classic "open the photo" transition.
   Customize: --vt-accent gradient, the group easing */

.nuda-vt-grid__stage { width: 240px; height: 150px; }

.nuda-vt-grid__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 8px;
  width: 100%;
  height: 100%;
}

.nuda-vt-grid__thumb { border-radius: 8px; background: #27272a; }

.nuda-vt-grid__thumb--hero {
  background: linear-gradient(135deg, #e4ff54, #a3e635);
  view-transition-name: vt-grid-hero;
}

.nuda-vt-grid__hero {
  display: none;
  width: 100%;
  height: 100%;
  border-radius: 12px;
  background: linear-gradient(135deg, #e4ff54, #a3e635);
  /* Same name as the thumb: only one is in the DOM at a time, so it morphs. */
  view-transition-name: vt-grid-hero;
}

.nuda-vt-grid__stage[data-view="detail"] .nuda-vt-grid__grid { display: none; }
.nuda-vt-grid__stage[data-view="detail"] .nuda-vt-grid__hero { display: block; }

.nuda-vt-grid__btn {
  background: #e4ff54;
  color: #09090b;
  border: none;
  border-radius: 8px;
  padding: 0.5rem 1rem;
  font-weight: 700;
  cursor: pointer;
}

.nuda-vt-grid__btn:focus-visible {
  outline: 2px solid #e4ff54;
  outline-offset: 2px;
}

::view-transition-group(vt-grid-hero) {
  animation-duration: 0.45s;
  animation-timing-function: cubic-bezier(0.34, 1.2, 0.64, 1);
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(vt-grid-hero) { animation: none; }
}
JavaScript
/* Grid to Detail — vanilla JS
   Toggles between the grid and the hero detail inside a view transition. */

(function () {
  document.querySelectorAll(".nuda-vt-grid").forEach(function (root) {
    var stage = root.querySelector(".nuda-vt-grid__stage");
    var btn = root.querySelector(".nuda-vt-grid__btn");

    function doSwap() {
      stage.dataset.view = stage.dataset.view === "grid" ? "detail" : "grid";
    }

    btn.addEventListener("click", function () {
      if (!document.startViewTransition) { doSwap(); return; }
      document.startViewTransition(doSwap);
    });
  });
})();

How to use Grid to Detail

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