details and summary
Native disclosure widgets with details/summary — open state, styling markers, a11y behavior, and when not to replace accordions with hacks.
- html
- details
- summary
<details> + <summary> are the platform’s disclosure widget: click/keyboard the summary to show or hide the rest. No JS required for the basic open/close pattern.
Docs: MDN <details>, <summary>.
Minimal pattern
<details>
<summary>Shipping options</summary>
<p>Standard (3–5 days), Express (1–2 days), Pickup.</p>
</details>
<details open>
<summary>Default open section</summary>
<p>…</p>
</details>
The open attribute reflects state; toggling updates it. Listen to toggle for analytics or accordion logic.
document.querySelectorAll("details").forEach((d) => {
d.addEventListener("toggle", () => {
if (d.open) console.log("opened", d.querySelector("summary")?.textContent);
});
});
Exclusive accordion (modern)
Multiple details can stay open by default. For exclusive groups, either:
- Small JS: on
toggle, close siblings - Or newer
nameattribute grouping on<details>where supported:
<details name="faq">
<summary>What is your refund policy?</summary>
<p>…</p>
</details>
<details name="faq">
<summary>Do you ship internationally?</summary>
<p>…</p>
</details>
Check current browser support for name before relying on it alone.
Styling
details {
border: 1px solid var(--border);
border-radius: 0.5rem;
padding: 0.5rem 0.75rem;
}
summary {
cursor: pointer;
font-weight: 600;
list-style: none; /* may need vendor pseudo for marker */
}
summary::-webkit-details-marker {
display: none;
}
summary::before {
content: "▸";
display: inline-block;
margin-inline-end: 0.4rem;
transition: transform 120ms ease;
}
details[open] summary::before {
transform: rotate(90deg);
}
If you remove the default marker, ensure an open/closed visual cue remains.
Accessibility notes
- Summary is exposed as a disclosure control; users can activate with keyboard on supporting browsers.
- Don’t put interactive controls inside
summaryin confusing ways (nested buttons). - Content inside details when closed is generally not visible; don’t hide critical-only-in-closed content users must read.
- For complex accordion patterns with arrow-key roving tabindex, APG accordion may still need custom widgets — but try native first.
SEO / progressive enhancement
Content in <details> is still in the HTML document (unlike pure client-rendered empty shells). Crawlers may treat it as page content — don’t use details to “hide” spam.
Interview out-loud
“details/summary give a native expandable section with an open attribute and toggle events. I style carefully if I remove the marker, avoid nested interactive elements in summary, and use light JS only for exclusive accordion groups when the platform name grouping isn’t enough.”
Footguns
- Animating height closed/open without overflow care.
- Putting the only copy of important legal text only in a closed details on mobile.
- Custom click handlers that
preventDefaultand break native toggle. - Assuming identical marker styling everywhere.
- Using details as a tooltip or menus — wrong pattern.
Exclusive accordion JS fallback
const group = document.querySelectorAll('details[data-accordion="faq"]');
group.forEach((d) => {
d.addEventListener("toggle", () => {
if (!d.open) return;
group.forEach((other) => {
if (other !== d) other.open = false;
});
});
});
FAQ page structure
<section aria-labelledby="faq-h">
<h2 id="faq-h">FAQ</h2>
<details name="faq"><summary>…</summary><p>…</p></details>
<details name="faq"><summary>…</summary><p>…</p></details>
</section>
Keep answers in the HTML for SEO and no-JS users. Do not load FAQ bodies only after open unless you have a strong reason and a non-JS path.
Nested details
Nested disclosures are valid for hierarchical FAQs but easy to overuse. Keep nesting shallow. Ensure each summary’s text stands alone without relying on parent open state. Keyboard users should not need a map of nested triangles to find answers — prefer a flat FAQ with clear headings when content is long.
Related
Further reading
Related guides
- Accessibility Tree OverviewHow browsers build the accessibility tree from DOM and CSS — roles, names, states, what’s pruned, and how to inspect it in DevTools.
- Audio and Video ElementsNative audio/video — controls, sources, captions, autoplay policies, and accessibility requirements for media on the web.
- Autocomplete and Name Attributesname and autocomplete on form fields — password managers, autofill tokens, and why missing names break real users more than demos.
- Base Element and Relative URLsHow <base href> rewrites relative URLs for links, scripts, and forms — powerful for static hosts, dangerous when set accidentally.
- contenteditable Basicscontenteditable surfaces — what the browser gives you, sanitization, keyboard and a11y gaps, and when to pick a real editor library.