I've been on a bit of a minimalism kick lately. I've stopped reaching for React for every project, realizing that for a blog like this, raw HTML and CSS is usually enough. In fact, this entire website - content, styles, scripts included - is lighter than the gzipped react-dom package alone.

But there was one thing I missed from the React world: motion.

Static sites often feel "dead." You click a link, the screen goes blank, and the new content appears. I wanted it to feel alive, preserving context as the user navigates, but without bringing megabytes of javascript to hydrate a virtual DOM just to animate a div.

Enter the View Transitions API

Specifically, Cross-Document View Transitions. This API allows the browser to snapshot the old state and the new state, then morph between them. It’s native, hardware-accelerated, and requires zero client-side routing, truly magical.

NOTE

Unfortunately, browser support is not-so-good yet for cross-document view transitions, so if you are reading this in firefox, I'm sorry.

The Chrome team's intro covers the mechanics well, but here is how I'm using it to make this static site feel a little bit alive.

The setup is deceptively simple. You just tell the browser to look out for navigations by adding the css:

@view-transition {
  navigation: auto;
}

When user navigates from the "Home" page to the "All Posts" page, I didn't want a hard cut. I wanted the recent posts to physically travel to their new positions in the full list.

This is made possible with Shared Element Transitions. The concept is simple: if you give two elements on different pages the same view-transition-name, the browser matches them up and handles the geometry.

<!-- On both the Home page and the Blog List page --> 
<li style="view-transition-name: post-item-${slug}">
  <!-- The entire item morphs -->
</li>

Things are a bit more interesting when you click into a specific article. I wanted the title and date to translate from the item and settle into the header of the post.

To make this work, we need to be more granular. Instead of transitioning the whole item, let's assign specific names to the text nodes inside the card.

<!-- In the List Item -->
<li>
  <span style="view-transition-name: post-date-${slug}">2026-01</span>
  <span style="view-transition-name: post-title-${slug}">Introduction to View Transitions</span>
</li>
<!-- In the Blog Post Header -->
<article>
  <h1 style="view-transition-name: post-title-${slug}">Introduction to View Transitions</h1>
  <div class="post-meta">
    <i style="view-transition-name: post-date-${slug}">Published Jan. 2026</i>
  </div>
  <!-- rest of the content -->
</article>

Because the names match, the browser performs a smooth "morph" transition, scaling the font size and translating the position automatically. BROWSER DOES ALLAT FOR YOU, isn't that cool?