Smooth Accordion Expand/Collapse
Animate accordion panels from collapsed to expanded heights — the height-transition problem that has plagued web devs for a decade — using View Transitions snapshots.
Detailed Explanation
The Auto-Height Animation Problem
Animating from height: 0 to height: auto has been impossible in pure CSS forever. The workarounds — measuring with JavaScript, animating max-height, or using grid-template-rows: 0fr → 1fr — all have caveats. The View Transitions API sidesteps the problem entirely by snapshotting both states.
function toggle(panelId) {
document.startViewTransition(() => {
document.getElementById(panelId).hidden = !document.getElementById(panelId).hidden;
});
}
.accordion-panel {
view-transition-name: var(--panel-name);
}
::view-transition-old(.accordion-panel) {
animation: 200ms ease-out both shrink;
}
::view-transition-new(.accordion-panel) {
animation: 250ms ease-out both grow;
}
@keyframes shrink {
to { opacity: 0; transform: scaleY(0.5); }
}
@keyframes grow {
from { opacity: 0; transform: scaleY(0.5); }
}
The siblings shift smoothly
The killer feature: when the panel expands, all the sibling panels below it also animate to their new positions through the root cross-fade group. You did not write any code to make that happen — the API captures the entire viewport and animates every visible element.
Differentiating panels with view-transition-name
If two accordions are open simultaneously and you want each to animate independently, give each a unique view-transition-name like panel-faq-1, panel-faq-2. Without unique names, all panels share the root group and animate as one.
Pairing with the <details> element
The native HTML <details> element and View Transitions work together beautifully:
details.addEventListener('toggle', (e) => {
document.startViewTransition(() => { /* nothing! the toggle already happened */ });
});
Wait — that does not work because toggle fires after the DOM change. Use click on the <summary> and call preventDefault() to control the timing.
Use Case
FAQ sections, settings panels, hierarchical navigation menus, file tree viewers, and any disclosure widget where you want smooth height + sibling-shift animation without measuring DOM heights manually.