Key insights
- You can't animate
height: auto— the transition just snaps. Usedisplay: gridwithgrid-template-rowsgoing from0frto1fr, or measurescrollHeightand animate to a pixel value. - Drive the chevron rotation from the same timing curve as the panel. Even ~10 frames of lag between the two reads as broken, not smooth.
- Decide single vs multi open: an accordion lets one panel open at a time, a disclosure lets many stay open. Sequential steps stay single; FAQ lists let several breathe.
- The header is a
<button>, not a<div>. Wirearia-expandedto reflect state andaria-controlsto point at the panel, so Enter/Space toggle it and the focus ring shows. - When an item near the bottom expands, anchor the tapped header so the list doesn't jump under the user, and stagger the revealed content in.
Do / Don't
- Do: Drive chevron rotation and panel height from one shared timing curve so they move as a unit
- Do: Render the header as a real
<button>witharia-expandedandaria-controlswired to the panel - Do: Match open behavior to content — one-at-a-time for steps, many-open for FAQ lists
- Don't: Animate
height: autoand expect a transition — use grid rows or a measured pixel height - Don't: Let the chevron trail the panel; even a few frames of lag feels janky
- Don't: Let the list scroll-jump when a lower item expands — keep the tapped header anchored