Let's Play Accordion with the HTML details element

Managing content on a website can be challenging. You don't want to overwhelm your users with too much information at once. One helpful strategy is to place content in sections with show/hide functionality. A typical example would be an FAQ page with a list of expandable questions and answers.

We can use the native <details> HTML element to create such a disclosure widget. The element has been around for many years, but it used to be tricky to style and animate. Thanks to Interop 2025, browsers have greatly improved their implementations of the element and added new features.

An older man sitting on a chair in front of a doorway, playing accordion. Photo: © Jefferson Lucena / pexels.com

I'll quickly go over the basics of the native HTML element. Then we'll take a look at the new shiny features. 🤩

The <details> HTML element

The native details element creates a disclosure widget, also called an expansion panel. The information placed inside the element is visible only when the widget is toggled into an open state. We use the <summary> HTML element to provide a label for the widget. Here's an example:

<details> <summary>An interesting question?</summary> <p>A fascinating answer!</p> </details>

With just a few lines of code we get a fully accessible disclosure widget. It communicates its name, role and current status to assistive technologies and is fully operable with a keyboard.

New Features

Exclusive Accordions with the name attribute

An accordion component is a common UI pattern found on many websites. It consists of several disclosure widgets that individually can be expanded and collapsed.

A variation of the accordion pattern is the exclusive accordion, in which only one section can be open at the same time. Use the name attribute to turn a list of <details> elements into an exclusive accordion. Take a look at this code snippet from my CodePen demo:

<details name="faq"> <summary>What is the Doctor's real name?</summary> <p>I'll never tell!</p> </details> <details name="faq"> <summary>Who are the Daleks?</summary> <p>The Daleks are...</p> </details>

Now the individual disclosure widgets are linked. When you open one of the sections in the accordion, the browser will automatically close all other sections. Try it yourself:

Content Styling with ::details-content

You can easily style the expandable content of the <details> element using the new ::details-content CSS pseudo-element. Here's an example:

details::details-content { color: rgb(54, 69, 79); line-height: 1.3; padding-inline: 0.5rem; }

Furthermore, we can use the pseudo-element to animate the opening and closing of our disclosure widget. Our goal is to smoothly transition the content from zero height to its full height. To pull this off, we depend on the still experimental interpolate-size CSS property, which is only supported in Chromium browsers at the moment.

But don't worry! We can implement the animation as a progressive enhancement. Using the @supports at-rule, we only apply the animation when the browser supports it:

@supports (interpolate-size: allow-keywords) { details { interpolate-size: allow-keywords; &::details-content { height: 0; overflow-y: clip; transition: height 0.35s ease, content-visibility 0.35s ease allow-discrete; } } details[open]::details-content { height: auto; } }

I know, there's a lot going on in the CSS code above. I'll explain it step-by-step:

  1. By default, browsers don't support animations and transitions involving an intrinsic size value like auto. We enable this behavior by setting interpolate-size: allow-keywords.
  2. We set the initial height of the content to zero (height: 0) and clip any overflowing content.
  3. Using the transition shorthand, we define the animation of the height property. We also need to include the content-visibility property, which is automatically set by the browser when the details element is expanded or collapsed. See transition-behavior for more information.
  4. At last, we set height: auto for the content when the details element is in the open state.

Open my CodePen demo in Chrome or Edge to see the smooth animations for yourself. 😎

Custom Indicator for open/closed state

Browsers typically present the <details> element with a small triangle that rotates to indicate open/closed state. This indicator will appear differently across platforms and browsers.

To replace the default triangle with a custom indicator, we need to override the display: list-item value of the <summary> element. I use flexbox as this supports my desired layout. But you could also set display: block, for example.

In addition, we also need to hide the pseudo-elements for the default triangle. This includes ::-webkit-details-marker, which is used in the Safari browser:

details summary { display: flex; align-items: center; justify-content: space-between; gap: 1rem; &::marker, &::-webkit-details-marker { display: none; } }

Next, we define our custom indicator. I want to display an arrow icon that points downwards when the disclosure widget is closed. Thanks to modern CSS, we don't need any images or icon fonts. We can simply draw the shape using the ::after pseudo-element and the clip-path CSS property.

details summary::after { --shape-chevron-width: 1rem; --s: calc(var(--shape-chevron-width) / 4); content: ""; aspect-ratio: 7/5; background: currentColor; clip-path: polygon(0 0, 0 var(--s), 50% 100%, 100% var(--s), 100% 0, 50% calc(100% - var(--s))); flex-shrink: 0; width: var(--shape-chevron-width); }

I adapted a code example from the website CSS Shape. It offers tons of code snippets for pure CSS shapes made with a single-element and modern CSS. You should definitely check it out! 🥰

Finally, we want the arrow icon to point upwards when the disclosure widget is open. Of course, the transition should be animated. Here's the code:

details { summary::after { rotate: 0deg; transition: rotate 0.35s ease; } &[open] summary::after { rotate: -180deg; } }

Et voilà! We've replaced the default triangle with a custom arrow icon that looks the same on all platforms and browsers. Here's the final result:

Flexbox and Grid Layouts

Historically it wasn't possible to change the display type of the <details> element. This restriction has now been relaxed in all major browsers (e.g., Firefox 143).

That means, it's finally possible to use grid or flex layouts on the <details> element. Check out the article “More options for styling <details> on the Chrome Developer blog.

Conclusion

The <details> and <summary> elements are awesome! Thanks to the new features we can easily create disclosure widgets and even exclusive accordions with custom styling and animations.

This means: One more type of content we can build with native web features. No need for UI library components and their annoying tendency for breaking changes. I love the (native) web! ❤️

Posted on