Animated Accordion

An expand that animates without measuring anything.

← All motions

Animated Accordion

This is the single most common animation failure in AI-generated interfaces: a panel that opens by transitioning height from zero to auto. That transition does not run — auto is not an interpolatable length — so the panel either snaps open or, worse, the model measures the content in JavaScript and the layout jumps on resize. The grid technique below animates the real height without measuring anything.

trigger: interaction grid-template-rows: 0fr → 1fr no JavaScript measurement

Live demo

Because auto is a keyword, not a length. The browser resolves it at layout time, so there is no numeric start or end value for the transition to interpolate between. Every workaround that measures scrollHeight in JavaScript inherits the same problem later: the measurement goes stale when the viewport changes or the content reflows.

A grid row sized 0fr collapses to zero; the same row sized 1fr takes exactly the content height. Both are flexible lengths, so the browser can interpolate between them. The panel is a grid with one row, and the inner element carries overflow: hidden so the content is clipped while the row is short.

For a visual accordion, no — details and summary give you open and closed for free, and CSS can style the marker. What they do not give you is the height transition, because the browser toggles display rather than a height. The version here is a button plus a panel, with one line of JavaScript toggling a class and the ARIA attribute.

The prompt

Build an accordion with a height transition that animates smoothly.

Markup:
- Each item is a container holding a button and a panel.
- The button carries aria-expanded ("true" or "false") and aria-controls pointing at the panel id. The panel has role="region" and aria-labelledby pointing at the button id.
- The panel contains exactly one child element that holds the content. That inner element is what gets clipped.

CSS:
- The panel is display: grid with grid-template-rows: 0fr and overflow: hidden applied to the INNER child, not the panel.
- The open state sets grid-template-rows: 1fr.
- Transition grid-template-rows over 220 to 280ms with an ease-out curve. Add a small padding transition if the content has padding, because padding is not part of the grid track.
- Do NOT transition height, max-height, or use scrollHeight measurements. height: auto cannot interpolate, and max-height with a guessed value either clips long content or finishes early on short content.
- Animate the open/close icon with a transform, not by swapping text.

Behaviour:
- Clicking an open item closes it; clicking another opens that one. Decide explicitly whether the accordion is single-open or multi-open and implement only that.
- Keyboard: Enter and Space activate the button natively; Tab moves between headers; do not add custom key handlers beyond that.
- Under prefers-reduced-motion, remove the transition entirely so panels open instantly.

Constraints:
- Content inside a closed panel must not be reachable by Tab. Use inert or visibility on the inner element, not display: none on the panel (which would kill the transition).
- Long content must scroll the page rather than the panel; the panel grows to its natural height when open.

What actually makes it work

Compatibility and performance

AspectDetail
grid-template-rows transitionChrome 107+, Firefox 66+, Safari 16+. Older Safari needs a max-height fallback.
CostLayout on every frame while animating. Acceptable for one panel at a time; do not open twelve at once.
Alternativeinterpolate-size: allow-keywords makes height: auto animatable in Chromium 129+, but it is not in Safari or Firefox yet.
Nested panelsWorks, but the inner panel's 1fr resolves inside the outer one's track; test with long content before shipping.

One practical note: the animation is layout-driven, unlike the transform-based effects elsewhere in this library. That is fine for a single panel with a few hundred pixels of content, and it is the reason an accordion should never be the element you open on every scroll event.

When not to use it

Don't hide essential content behind it. Anything a visitor must read to complete a task should be visible. An accordion is for secondary detail, specifications and FAQs.

Don't put more than one long panel in the same view. Opening a 2,000px panel pushes everything below it out of sight, and the visitor loses the header they just clicked.

Don't use it for navigation. A menu that expands in place is not a disclosure; it needs different focus management and different semantics from a FAQ accordion.