Animating height directly forces the browser to recalculate layout on every frame and breaks at auto values. Animating grid-template-rows from 0fr to 1fr moves the work to compositing — smooth at 60 fps, no jank, and auto-height content works without hardcoding a max-height. The hidden child needs overflow: hidden and min-height: 0 so the row collapse actually clips it.
This menu opens one item at a time. Multi-open is more flexible; single-open is more focused. For navigation menus and FAQs the focus matters more than the flexibility — readers skim, find one thing, close it before moving to the next. Multi-open is the right default only when comparing items side by side is the primary task.
A monospaced + that becomes − on open. No rotating chevron, no SVG icon — the symbol carries the state in its own form. The character changes; the typographic register stays consistent with the rest of the lab. One less asset to load, one less DOM node to animate.
Every header is a real <button> with aria-expanded reflecting state. Keyboard activation is free (buttons handle Enter and Space natively). The amber focus ring comes from the global :focus-visible rule. No JavaScript was added for accessibility — the right element type carried it.