Animated Accordions without JS

2025/12/13

While working on this site, I wound up using the HTML <details> and <summary> tags. They’re quite neat if you’re trying to go by with as little client-side JS as possible, but the only downside is that they don’t support transitions out of the box, or atleast it seems complicated.

A simple search brought up the humble MDN page:

::details-content - CSS | MDN The ::details-content CSS pseudo-element represents the expandable/collapsible contents of a <details> element.
https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/::details-content

which demonstrates adding a smooth fade-in/out animations on the details content using the ::details-content pseudo selector (baseline 2025). This was very neat and can be applied by simply following the guide they gave. It looked good and is something I did end up using in the final version.

I wanted to implement a height-based transition as well. We know that animating height is always a pain due to the inability to animate from height: 0 to height: auto, however, this time around I stumbled upon this excellent article:

🧙‍♂️ CSS trick: transition from height 0 to auto! If you messed around with CSS for long enough, chances are you've tried at least once to make a... Tagged with webdev, frontend, css.
https://dev.to/francescovetere/css-trick-transition-from-height-0-to-auto-21de

But the technique discussed there by setting display: grid and animating the grid-template-rows didn’t seem to work here (possible skill issue). Finally, I found this article:

Animate to height: auto; (and other intrinsic sizing keywords) in CSS | CSS and UI | Chrome for Developers
https://developer.chrome.com/docs/css-ui/animate-to-height-auto

This was a long-needed feature and this is how I used it to create the final effect:

<details>
  <summary>Summary</summary>
  <ul>
    <li>First list item</li>
    <li>Second list item</li>
    <li>Third list item</li>
  </ul>
</details>
/* required for animating height:auto */
:root {
  interpolate-size: allow-keywords;
}

details::details-content {
  height: 0;
  opacity: 0;
  overflow: hidden;
  transition:
    height 300ms,
    opacity 300ms,
    content-visibility 300ms allow-discrete;
}

details[open]::details-content {
  height: auto;
  opacity: 1;
  content-visibility: visible;
}

…and that’s it! See it in action below:

See the Pen Better Accordion no JS by priyansh agrahari (@r3dacted42) on CodePen.

Thanks for reading! Feedbacks and suggestions welcome :)