🚀AdvancedAdvanced Interactive Elements

Details & Summary Elements

Build native disclosure widgets, accordions, and expandable sections with <details> and <summary> no JavaScript required.

Overview

The `<details>` and `<summary>` elements provide a native browser disclosure widget. The `<summary>` acts as the visible toggle label; everything else inside `<details>` is the expandable content. Clicking the summary toggles the `open` attribute on the details element, showing or hiding the content no JavaScript needed. Beyond simple FAQ items, these elements can be composed into accordion patterns, used for navigation drawers, code snippet expanders, and filter panels. The `toggle` event fires whenever the open state changes, enabling progressive enhancement with JavaScript when needed. The default marker (triangle arrow) can be styled with the `::details-marker` pseudo-element (or `::marker` in most browsers), and the summary can be given any appearance using CSS it behaves like a button semantically while looking like anything visually.

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.

Loading editor…

Completed: 0 / 5 exercises

Knowledge Check

Question 1 of 4

Where must the <summary> element be placed inside <details>?