Element-Scoped View Transitions: Element.startViewTransition()
Morphing list items without rerendering the page
Animating list reorder with the FLIP technique meant measuring every item, applying the change, measuring again, and animating the delta. Element-scoped view transitions run the entire transition on one subtree of the DOM and leave everything else interactive.
function reorder(items) { const positions = new Map(); document.querySelectorAll( '.grid li').forEach( el => { positions.set(el.id, el.getBoundingClientRect()); }); applyNewOrder(items); document.querySelectorAll( '.grid li').forEach( el => { const before = positions .get(el.id); const after = el .getBoundingClientRect(); const dx = before.left - after.left; const dy = before.top - after.top; el.animate( [ { transform: `translate(${dx}px,${dy}px)` }, { transform: 'translate(0,0)' } ], { duration: 300, easing: 'ease' }); });} .grid li { view-transition-name: match-element;}::view-transition-group(*) { animation-duration: .3s;} view transitions element scoped Browser Support
This feature is not Baseline because it does not work in some of the most widely-used browsers.
Not ready for production without a fallback.
Scoped to a subtree
Only the element you call startViewTransition on captures snapshots. The header, navigation, and everything else outside the scope stay interactive during the animation.
No FLIP recipe
No measuring positions before, applying the DOM change, measuring after, and animating the delta. The browser snapshots the subtree and tweens between states.
Multiple at once
Run scoped view transitions on different parts of the page in parallel. Filter a list and sort a sidebar simultaneously without one blocking the other.
How it works
Animating a list reorder, a filter, or a sort used to mean writing the FLIP technique: measure every item's position before the change, apply the DOM change, measure every item's position after, then animate each one from the delta. It worked, but the math lived in JavaScript and the rest of the page got snapshotted along with the list when you used a global view transition.
Element-scoped view transitions move that work into the browser and limit it to one part of the DOM. Call element.startViewTransition(callback) on the grid (or sidebar, or any subtree). The browser snapshots only that subtree, runs the callback to apply the change, snapshots again, and animates between the two. The navigation, the header, every other widget on the page stays interactive while the list animates. Multiple scoped transitions can run in parallel, since each one only locks its own subtree.
view-transition-name still tells the browser which elements to match between snapshots. Inside a scoped transition, the resulting ::view-transition pseudo-elements are injected onto the scope root, not the document, and view-transition-scope: all is applied automatically to keep names from leaking out of the subtree.
prefers-reduced-motion when you wrap the ::view-transition pseudo-elements in a media query, just like any other animation. Disable or shorten the motion for users who request it.