Details & Summary Elements
Build native disclosure widgets, accordions, and expandable sections with <details> and <summary> no JavaScript required.
Overview
Key Points
- •`<details>` is closed by default; add the `open` attribute to start expanded
- •`<summary>` is the clickable label it must be the first child of `<details>`
- •The `toggle` event fires on `<details>` whenever it opens or closes
- •Use `name` attribute on multiple `<details>` elements to create exclusive accordions (only one open at a time)
- •The `::details-marker` pseudo-element (or `list-style` on summary) controls the triangle indicator
- •Screen readers expose `<details>` as a disclosure widget with the summary as its button label
- •Nesting `<details>` inside `<details>` creates multi-level collapsible trees
Examples
Basic FAQ disclosure widget
<details>
<summary>What is the return policy?</summary>
<p>
You can return any item within 30 days of purchase for a full refund.
Items must be in their original condition and packaging.
</p>
</details>
<details open>
<summary>Is shipping free?</summary>
<p>Free shipping on all orders over $50. Standard shipping is $4.99.</p>
</details>
<style>
details {
border: 1px solid #e2e8f0;
border-radius: 8px;
padding: 0.5rem 1rem;
margin-bottom: 0.5rem;
}
summary {
font-weight: 600;
cursor: pointer;
list-style: none; /* remove default marker */
padding: 0.5rem 0;
display: flex;
justify-content: space-between;
align-items: center;
}
summary::after {
content: '+';
font-size: 1.25rem;
}
details[open] summary::after {
content: '−';
}
</style>The `open` attribute on the second details element makes it start expanded.
Exclusive accordion using the name attribute (one open at a time)
<!-- name="faq" groups these opening one closes the others -->
<details name="faq">
<summary>Section 1: Getting Started</summary>
<p>Content for getting started section.</p>
</details>
<details name="faq">
<summary>Section 2: Advanced Features</summary>
<p>Content for advanced features section.</p>
</details>
<details name="faq">
<summary>Section 3: Troubleshooting</summary>
<p>Content for troubleshooting section.</p>
</details>The name attribute creates an exclusive accordion group natively no JavaScript needed.
Toggle event for progressive enhancement
<details id="code-example">
<summary>Show Code Example</summary>
<pre><code id="code-block">Loading...</code></pre>
</details>
<script>
const details = document.getElementById('code-example');
let loaded = false;
details.addEventListener('toggle', async () => {
if (details.open && !loaded) {
loaded = true;
// Lazy-load the code only when expanded
const response = await fetch('/api/code-snippet');
const code = await response.text();
document.getElementById('code-block').textContent = code;
}
});
</script>Progressive enhancement: lazy-load content inside details only when the user opens it.
Common Mistakes
- Putting the summary anywhere other than as the first child of details it must be the direct first child
- Using JavaScript to toggle details when the browser already handles it natively
- Forgetting that `list-style: none` on `<summary>` removes the default arrow marker in WebKit/Blink
Note
In 2024, the `name` attribute for exclusive accordions landed in all major browsers. Before using it, confirm your target browser support or add a small JS fallback for older browsers.
Exercises
Write HTML to solve each exercise. Use Preview to see it render in a live iframe, then mark as done.
Completed: 0 / 5 exercises
Knowledge Check
Question 1 of 4