How to make an accordion
Short answer
The details tag opens on a click on summary — without a single line of JavaScript:
<details>
<summary>How long is delivery?</summary>
<p>Usually two to four working days.</p>
</details>
<details>
<summary>Can I return an item?</summary>
<p>Yes, within 30 days of purchase.</p>
</details>Only one open
The same name attribute on several details makes them a group: opening one closes the others:
<style>
.faq { border-bottom: 1px solid gainsboro; padding: 8px 0; }
.faq summary { cursor: pointer; font-weight: 600; list-style: none; display: flex; justify-content: space-between; }
.faq summary::after { content: "+"; }
.faq[open] summary::after { content: "−"; }
</style>
<details class="faq" name="faq" open><summary>Shipping</summary><p>Free from $50.</p></details>
<details class="faq" name="faq"><summary>Payment</summary><p>Cards and PayPal.</p></details>
<details class="faq" name="faq"><summary>Returns</summary><p>Within 30 days.</p></details>list-style: none removes the default triangle, and ::after draws a custom icon that changes on an open section [open].
Smooth opening
details used to open only with a jump. In new browsers smoothness comes from interpolate-size: allow-keywords together with a transition on the ::details-content pseudo-element; elsewhere the section simply opens at once.
Common mistake
People build an accordion from a div with a click handler. Such a block does not open from the keyboard, and a screen reader does not know it expands. details and summary give you all this for free — use them.
Functions used in this recipe
- <details>, <summary> — A disclosure block without JavaScript: `<summary>` is the visible title, the rest is hidden until clicked.
- transition — Animates the change of a property from the old value to the new one.
- ::marker — The bullet or number of a list item.