An animated FAQ accordion in pure HTML

An animated FAQ accordion in pure HTML Set markodenic.tech as your preferred Google source

The FAQ section is one of those things that always turns into JavaScript. Click handlers, an open index in state, aria-expanded toggling, a height measured and animated by hand, and a bug where two panels open at once.

The browser has shipped every piece of this. The markup is <details>, the “only one open at a time” behavior is one attribute, and the animation is now three lines of CSS.

The fix

html
<details name="faq">
  <summary>Do you offer refunds?</summary>
  <p>Yes, within 30 days, no questions asked.</p>
</details>

<details name="faq">
  <summary>Can I change plans later?</summary>
  <p>Any time, and we adjust the invoice automatically.</p>
</details>

Two <details> elements sharing a name form an exclusive accordion: opening one closes the other, the same way radio buttons work. No JavaScript, no state, and keyboard support and the correct accessibility semantics come with it.

Then the part that used to be impossible:

css
:root {
  interpolate-size: allow-keywords;
}

details::details-content {
  block-size: 0;
  overflow: hidden;
  transition: block-size 0.3s ease, content-visibility 0.3s allow-discrete;
}

details[open]::details-content {
  block-size: auto;
}

That’s a real height animation, on content whose height you never measured.

Do you offer refunds?

Yes, within 30 days, no questions asked. Email us and the money is back on your card in a few working days.

Can I change plans later?

Any time. Upgrades take effect immediately and we adjust the next invoice for the days you already paid for. Downgrades apply at the end of the current billing period.

Do you offer a team plan?

Teams of three or more get shared billing, a single invoice, and the ability to move a seat between people without losing any work.

No JavaScript on this page. Opening one panel closes the other because they share the same name. Where the animation is not supported yet, panels open instantly.

Why this used to need JavaScript

You cannot animate to height: auto. That single limitation is the reason every accordion library exists: they measure scrollHeight, set a pixel height, animate to it, then set it back to auto so the panel can reflow if the content changes.

interpolate-size: allow-keywords removes the limitation. It tells the browser it may interpolate to keyword sizes like `auto`, min-content, and fit-content. Set it once on :root and it applies everywhere.

::details-content is the other new piece: a pseudo-element for the part of a <details> that gets revealed, which previously had no selector at all. That’s why the old trick was wrapping the content in an extra <div>.

The allow-discrete on the transition covers content-visibility, which flips between visible and hidden with no in-between values. Without it, the panel’s content disappears the instant you close it and the height animates on an empty box.

Styling the summary

The default marker is a small triangle you’ll want to replace:

css
summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  padding: 1rem 0;
  font-weight: 600;
}

summary::-webkit-details-marker {
  display: none;
}

summary::after {
  content: '+';
  font-size: 1.25rem;
  transition: rotate 0.3s ease;
}

details[open] summary::after {
  rotate: 45deg;
}

A plus that rotates 45 degrees into a cross, in four lines. list-style: none removes the marker in modern browsers, and the -webkit rule covers older Safari.

Where this comes up

  • FAQ sections: on every landing and pricing page you will ever build.
  • Filter panels: collapsible groups in a sidebar.
  • Long forms: optional sections that stay out of the way until needed.
  • Docs: “show the full example” blocks under a short answer.

Browser support

<details name="..."> is supported everywhere now, so the exclusive accordion is safe to ship today.

The animation is newer, and its two pieces have different support. ::details-content is in all three engines: Chrome 131, Safari 18.4, Firefox 143. interpolate-size is Chromium-only, with no implementation yet in Safari or Firefox. Where it’s missing, the panel opens and closes instantly, which is precisely what your accordion does today. There is no broken state to design around, which is what makes this worth shipping now rather than in two years.

If you want to be explicit about it:

css
@supports (interpolate-size: allow-keywords) {
  /* the transition rules */
}

Why this matters

  • This is a deletion, not a rewrite: the click handlers, the open index, the `aria-expanded` bookkeeping, the measured heights, all of it goes.
  • The keyboard and the semantics come with it: those are the parts hand-rolled accordions get wrong, and there’s nothing left to get wrong.
  • A closed panel is still real content: Google indexes it, and Ctrl+F finds text inside it and opens the right one. Content rendered on click is invisible to both.

Open the FAQ on whatever site you’re working on now. Count the lines of JavaScript keeping it open and closed, then delete them.

Happy coding!

Marko

Rate this issue

Your ratings shape which issues I recommend to you.