HTML Details Table

PieceWhat it doesField note
<details> + <summary>Native disclosureZero JS accordion - keyboard and screen reader for free
open attributeStart expanded / controlBoolean attr; toggle from JS = details.open = true
toggle eventReact to expand/collapseLazy-render content on first open - cheap accordions
name="accordion"Exclusive groupSame name = opening one closes the others (2024 baseline)
summary ::markerThe triangleHide with list-style:none; build your own with ::after rotation
content-visibility trickHide, not unmountClosed details keep DOM - heavy inner content still costs render unless cv:auto
vs tabsAll visible vs one visibleDetails stack vertically, all content findable - better for FAQ/SEO
print stylingdetails[open] forcedForce open in print CSS - collapsed answers do not print
Reference: the MDN details element reference. details/summary is the native accordion: zero JavaScript, keyboard operable, screen-reader complete - the component every custom accordion rebuilds badly. The modern completion: the name attribute groups details into an exclusive accordion (opening one closes the others), and the toggle event enables lazy rendering of expensive content on first open. Bottom line: FAQ pages belong in details (all answers exist in the DOM for crawlers, unlike tab panels), the triangle is just ::marker (hide and rebuild it), and print styles should force details[open]. Related tools: form elements table (the other native interactive set), pseudo-elements table (the marker styling), and event listeners table (the toggle event wiring).

details/summary is the native accordion: zero JavaScript, keyboard operable, screen-reader complete. Every custom accordion on the web re-implements what this element ships with by default - focus handling, state semantics and the disclosure triangle included.

Bottom line: the name attribute (2024 baseline) completes the pattern - details elements sharing one name form an exclusive accordion where opening one closes the others, no JavaScript. The toggle event then enables lazy rendering: expensive inner content renders on first open, not on page load.

The honest part: a closed details element still costs its DOM. The content is hidden, not unmounted - heavy tables or hundreds of nodes inside closed details still parse, still sit in memory, and still may cost render unless content-visibility handles it. Lazy-render via the toggle event is the actual performance tool.

How to use

  1. Build the FAQ block: <details><summary>Question</summary>Answer</details> - crawlers see all answers (unlike tab panels), users click what they need.
  2. Group into a real accordion: give every details the same name="faq" - exclusive open/close behavior arrives as plain HTML in 2024-baseline browsers.
  3. Lazy-render the heavy part: details.addEventListener('toggle', () => { if (details.open && !rendered) renderExpensiveTable(); }) - first open pays, page load does not.

Frequently asked questions

When are details/summary better than tabs - and when are tabs right?

Details wins for content that is a LIST of independent Q&A items: everything stays in the DOM and findable (Ctrl-F works across all answers, crawlers index all answers, printing can include everything), and the vertical stack works at any width. Tabs win when the panes are alternate views of the SAME object - map view versus list view - where only one can be meaningful at a time and state lives in the selection. The SEO tiebreaker: hidden tab panels are frequently deprioritized or treated inconsistently by crawlers, while details content is unambiguous. FAQ, spec sections and settings groups are details territory.

How does the name attribute change the accordion game?

Before it, exclusive accordions (open one, close the rest) required JavaScript watching sibling toggles - the most-copied snippet in accordion tutorials. With name="faq" on every details in the group, the browser enforces it natively: opening one closes the others, and it degrades gracefully (older browsers just allow multiple open, which remains valid UI). Two details with the same name must be siblings in the same group scope; different groups use different names. The 2024 baseline pairing - name for exclusivity plus toggle for lazy render - removes the last two reasons hand-rolled accordions existed.

How do I restyle or remove the disclosure triangle?

It is a marker: summary { list-style: none } removes the triangle (that is the cross-browser incantation, plus ::-webkit-details-marker for older Safari). Rebuild the affordance yourself - a summary::after with a rotating chevron driven by details[open] is the common pattern, and it doubles as the animation hook. Accessibility note: the triangle is redundant for screen readers (state is announced), but sighted keyboard users need SOME visual open indicator - a rotation, a color change, an underline. Removing the marker without adding a replacement state cue fails the affordance, not just the aesthetics.

Why is my closed details content still slow?

Because closed is visual, not computational. The content stays parsed and in the DOM; styles apply, layout is skipped for display:none-like internals but expensive things - big tables, hundreds of nodes, images - still occupy memory and some render cost. The two-tier fix: content-visibility: auto on the inner container lets the browser skip rendering until relevant, and the toggle-event lazy render goes further - the expensive DOM does not exist until first open. For truly heavy embeds (maps, charts), lazy render is the only approach that actually removes the page-load cost.

Related tools