Animated Tab Switch with View Transitions
Cross-fade between tab panels and animate the active-tab indicator pill smoothly across the tab bar using a single shared view-transition-name.
Detailed Explanation
Two Animations, One Transition
A polished tab UI has two synchronized animations: (1) the active-tab indicator pill sliding to the new tab, and (2) the panel content cross-fading. Both fall out of a single startViewTransition call.
<div class="tabs">
<button class="tab" data-active="true">A</button>
<button class="tab">B</button>
<div class="indicator"></div>
</div>
<div class="panel">…content for active tab…</div>
.indicator {
view-transition-name: tab-indicator;
}
.panel {
view-transition-name: tab-panel;
}
function selectTab(target) {
document.startViewTransition(() => {
document.querySelectorAll('.tab').forEach(t => t.dataset.active = 'false');
target.dataset.active = 'true';
moveIndicatorTo(target); // sets .indicator's left + width
swapPanelContent(target.dataset.panelId);
});
}
The browser captures the indicator at its old position, animates the snapshot to the new position, and cross-fades the panel content in parallel. No FLIP math required.
Why this beats CSS transition: left, width
A pure CSS transition on the indicator works only if you keep the same DOM element across renders. As soon as you re-render the tab list (common in React when filtering tabs), the transition resets. View Transitions snapshot the visual position regardless of DOM identity, so re-renders animate correctly.
Performance: keep the panel content lightweight
Snapshotting a panel with thousands of DOM nodes is slower than snapshotting a panel with a hundred. If the panel contains a virtualized list, scroll position will reset on re-render — capture it before the transition and restore after transition.finished.
Use Case
Settings UIs, dashboards with metric panels, code editors with file tabs, browsers' devtools panels, and any tabbed interface where you want both indicator and content animations without writing FLIP code.