LEARNING OBJECTIVES โต
- Understand the View Transitions API lifecycle and how the browser captures snapshot states across DOM mutations.
- Master the pseudo-element tree:
::view-transition,::view-transition-group(),::view-transition-old(), and::view-transition-new(). - Implement Cross-Document Multi-Page App (MPA) View Transitions using the CSS
@view-transitionrule andpageswap/pagerevealevents. - Build shared-element morphing animations (e.g., thumbnail-to-hero expansions) with strict
prefers-reduced-motionaccessibility fallbacks.
๐ The Mental Model & Story (Intuitive Foundation)
Think about the difference between watching a traditional slide-projector slideshow versus watching an Apple iOS photo album transition.
In a traditional slide projector (the classic web page navigation), when the presenter advances to the next slide, the screen flashes completely pitch black for 200 milliseconds. The old image vanishes abruptly, and the new image suddenly appears. If slide 1 had a small photo of a camera and slide 2 showed that same camera blown up in full detail, your brain had to reorient itself because the visual connection was severed during the dark flash.
TRADITIONAL WEB NAVIGATION:
[Page 1: Product Grid] ===> [FLASH OF WHITE SCREEN / TEAR] ===> [Page 2: Product Detail]
(Visual continuity is broken; the user loses spatial context)
Now consider the View Transitions API (the iOS cinematic model). When you tap a product thumbnail, the browser pauses rendering for a fraction of a millisecond to take a photographic snapshot of the current DOM (the "old" state). The DOM is then updated to the new page structure, and the browser takes a snapshot of the "new" state. Finally, the browser creates an overlay with both snapshots and smoothly morphs, slides, or scales the elements into place. The thumbnail appears to physically expand and fly into the full hero banner.
VIEW TRANSITIONS API:
[Page 1: Thumbnail] โโ(Freeze snapshot)โโ> [DOM Update] โโ(Morph old -> new)โโ> [Page 2: Hero Banner]
(Smooth 60fps hardware-accelerated morphing with full spatial continuity)
Best of all, this is no longer restricted to complex Single Page Applications with giant animation libraries. Modern HTML & CSS living standards support this natively across regular multi-page hyperlinks!
Technical Deep Dive & Specifications
The View Transitions Lifecycle & Pseudo-DOM Tree
When document.startViewTransition(updateCallback) is invoked:
- The browser captures a live raster snapshot of all elements with an assigned
view-transition-name. - The browser renders the
updateCallback()function (which modifies the DOM, adds/removes elements, or updates text). - The browser captures the new raster snapshot of the updated DOM.
- The browser constructs a top-level pseudo-element tree in the document root and executes CSS animations.
::view-transition (Root container overlay)
โโโ ::view-transition-group(root)
โโโ ::view-transition-image-pair(root)
โโโ ::view-transition-old(root) <--- Snapshot of old DOM state (Fades out)
โโโ ::view-transition-new(root) <--- Snapshot of new DOM state (Fades in)
โโโ ::view-transition-group(product-hero)
โโโ ::view-transition-image-pair(product-hero)
โโโ ::view-transition-old(product-hero) <--- Thumbnail snapshot (Scales up)
โโโ ::view-transition-new(product-hero) <--- Detail hero snapshot
Pseudo-Element Hierarchy Reference
| Pseudo-Element | Purpose & Role | Default CSS Behavior |
|---|---|---|
::view-transition |
Top-level overlay canvas covering the viewport. | position: fixed; inset: 0; pointer-events: none; z-index: 2147483647; |
::view-transition-group(name) |
Container for a specific transition group (animates size & position). | animation: -ua-view-transition-group-anim; |
::view-transition-image-pair(name) |
Isolation wrapper grouping old and new visual snapshots. | isolation: isolate; |
::view-transition-old(name) |
The visual image snapshot of the DOM before the transition. | animation: 0.25s ease both -ua-mix-blend-mode-plus-lighter, -ua-view-transition-old-fade-out; |
::view-transition-new(name) |
The live visual image of the DOM after the transition. | animation: 0.25s ease both -ua-mix-blend-mode-plus-lighter, -ua-view-transition-new-fade-in; |
Multi-Page App (MPA) Cross-Document Navigation
Starting in modern Chromium and WebKit engines, cross-document view transitions work across standard HTML page links (<a href="detail.html">) without SPA routing.
To enable cross-document view transitions, simply declare the CSS @view-transition at-rule in your global stylesheet:
@view-transition {
navigation: auto; /* Enables seamless cross-document page transitions */
}
When the user clicks a link from /products to /products/42, the browser automatically takes snapshots of the exiting document and morphs elements with matching view-transition-name values into their positions on the incoming document.
๐ป Interactive Code Playground
Here is a complete, interactive Single Page & Shared Element View Transition demo. Tap between "List View" and "Detail View" to observe the card expanding smoothly into a full hero view using native CSS snapshots.
Starter Code
Line-by-Line Code Breakdown
- Lines 23โ31 (
view-transition-name: hero-image): Uniquely tags the image element. The browser pairs this snapshot with any element in the subsequent DOM tree that also sharesview-transition-name: hero-image, generating smooth position and size interpolation. - Lines 34โ38 (
::view-transition-old(hero-image)): Customizes the easing curve and duration specifically for the hero image transition group, overriding the browser's default cross-fade. - Lines 41โ47 (
@media (prefers-reduced-motion: reduce)): Essential accessibility safeguard. Disables all transition animations when the user has expressed a preference for reduced motion in their operating system settings. - Lines 108โ117 (
document.startViewTransition(...)): Calls the browser API. The browser freezes the viewport, captures the list card state, executes theupdateDOMcallback, captures the expanded detail view, and runs the animation pipeline.
Expected Browser Render Output
[Catalog View]
CyberWear Store
+------------------------------------+
| [Photo: Titanium Smart Mug (180px)]|
| Titanium Smart Mug |
| $129.00 USD |
+------------------------------------+
(User clicks the card -> The image smoothly expands from 180px to 350px width/height while the title shifts into the header position)
[Detail View]
[โ Back to Catalog]
+------------------------------------------------------+
| [Photo: Titanium Smart Mug (Expanded Hero 350px)] |
| Titanium Smart Mug (Large Heading) |
| Crafted from aerospace-grade titanium... |
| $129.00 USD |
| [Add to Cart Button] |
+------------------------------------------------------+๐๏ธ Hands-On Exercise
๐ฏ The Challenge: Circular Clip-Path Theme Switcher with View Transitions
Instructions:
- Build a Dark/Light mode theme toggle using
document.startViewTransition(). - Calculate the click coordinates (
event.clientX,event.clientY) where the user clicks the theme button. - Compute the maximum distance to the furthest screen corner using the Pythagorean theorem (
Math.hypot). - Animate the
::view-transition-new(root)pseudo-element usingdocument.documentElement.animate()with a expanding circularclip-pathstarting from the click point:circle(0px at ${x}px ${y}px)expanding tocircle(${maxRadius}px at ${x}px ${y}px).
๐ Starter Code Sandbox
โ ๏ธ Common Pitfalls
- Duplicate
view-transition-nameValues: Everyview-transition-namevisible on the page simultaneously must be strictly unique! If two elements on the same page shareview-transition-name: item-title, the browser aborts the transition with a console error:Transition rejected: duplicate transition name. - Neglecting
prefers-reduced-motion: Vestibular motion disorders can cause severe nausea when large screen elements fly or zoom across the viewport. Always wrap view transition animation rules in@media (prefers-reduced-motion: reduce)to disable them for sensitive users. - Blocking Async Callbacks: Passing an async function to
document.startViewTransition(async () => { await fetch(...) })freezes the old screen state until the network response arrives. Always fetch your data before invokingstartViewTransition, keeping the callback strictly for synchronous DOM mutation.
๐ก Pro Tips
- Use Scoped Transition Classes: Apply a class to
document.documentElement(e.g.<html class="back-transition">) immediately before triggeringstartViewTransitionto dynamically switch between forward slide and backward slide CSS animations. - Combine with Navigation API: Pair
document.startViewTransition()with the modernwindow.navigation.addEventListener('navigate', (e) => ...)to enable seamless, single-line SPA routing across all anchor links.
๐ Key Takeaways
- The View Transitions API provides native browser snapshotting and hardware-accelerated interpolation between DOM states.
- Elements assigned matching
view-transition-namevalues are automatically sized, repositioned, and morphed across transitions. - The
::view-transitionpseudo-element tree allows full customization of fade, slide, scale, and clip-path animations using standard CSS. - Cross-document Multi-Page transitions are activated using the CSS rule
@view-transition { navigation: auto; }. - Never fetch network data inside
startViewTransition(); keep the callback purely dedicated to instant DOM manipulation. - --