details & summary
The `<details>` and `<summary>` elements give you a native, keyboard-accessible disclosure widget with no JavaScript. The browser handles open/close toggling, focus management, and the toggle event.
What details and summary do
The <details> element is a disclosure widget: it has two states, collapsed and expanded, and toggles between them when the user interacts with its <summary> child. In the collapsed state only the summary is visible. In the expanded state the full content appears. The open boolean attribute controls which state the element is in -- when present, it is expanded. You can set it in HTML (<details open>) to start a widget already open. JavaScript can read and set el.open as a boolean property.
The <summary> must be the first child of <details>. It is the visible label that acts as the toggle button -- the browser gives it role="button" automatically, so it receives keyboard focus, can be activated with Space or Enter, and is announced correctly by screen readers. If you omit <summary>, browsers generate a fallback label that typically reads "Details". The triangle marker before the summary is a CSS ::marker pseudo-element and can be replaced or hidden.
Why native beats a custom accordion
A hand-rolled accordion built from <div> elements requires you to manage: a button with correct aria-expanded and aria-controls, a panel with id and correct hidden state, keyboard event handlers for Space and Enter, focus trapping or not, and open/closed state in JavaScript. The <details> element handles all of this for free. The browser manages aria-expanded, the toggle event, keyboard activation, and focus order. Content inside an open <details> is automatically in the tab order; content inside a closed one is not.
The name attribute (supported in modern browsers from 2024) creates an exclusive accordion: multiple <details> elements with the same name value behave like a radio group -- opening one closes the others. This replaces an entire class of JavaScript that developers have been writing for decades.
How to use details and summary
The minimal pattern is <details><summary>Question</summary><p>Answer</p></details>. To start it open, add the open attribute: <details open>. To listen for state changes, add an event listener for the toggle event: el.addEventListener('toggle', () => { if (el.open) { ... } }). The event fires after the state has changed.
For an exclusive accordion where only one panel can be open at a time, give all <details> elements the same name attribute: <details name="faq">. The browser enforces mutual exclusion. For an animated version, listen for the toggle event and use GSAP (or a CSS max-height transition) to animate the content region.
To customise the marker, target summary::marker { content: '' } or summary { list-style: none } (for webkit compatibility) and add your own icon. You can also use summary::-webkit-details-marker { display: none } for older Safari.
Try it
Click summaries to open and close disclosure panels, and toggle exclusive mode to see the name attribute enforce a single-open accordion.
What is the difference between defer and async?▾
defer downloads in parallel and executes in order after HTML parsing. async downloads in parallel and executes immediately when done, in no guaranteed order.
When should I use <summary> vs a custom button?▾
Use <summary> inside <details> for disclosure widgets. The browser handles role=button, keyboard activation (Space/Enter), aria-expanded, and focus management automatically.
Can I animate the open/close transition?▾
Yes. Listen for the toggle event on the <details> element. When el.open is true, GSAP-animate the content from height 0 to auto. When false, animate back to 0, then remove the open attribute.
Open state
HTML pattern
<!-- Basic --> <details> <summary>Question</summary> <p>Answer</p> </details> <!-- Exclusive accordion (name groups them) --> <details name="faq">...</details> <details name="faq">...</details> <!-- Start open --> <details open>...</details>
Check yourself
Pick an answer to lock it in, then read why. Getting one wrong is part of how it sticks.
Remember this
<summary>must be the first child of<details>. Content comes after it.- The
openattribute controls state. Add it to HTML for an initially open widget. Read/setel.openin JS. namecreates an exclusive accordion group: only one<details>with that name can be open at a time.- The
toggleevent fires after state changes -- use it to trigger GSAP animations.
Done with this concept?
Mark it complete to track your progress. No login needed.