PureDevTools

HTML Details & Summary Reference

Complete reference for the HTML details and summary disclosure elements — behavior, events, accordion, styling, and accessibility

All processing happens in your browser. No data is sent to any server.
13 entries
|

You’re building an FAQ section and reach for a JavaScript accordion library — 15 KB minified, custom event handlers, ARIA attributes you have to manage manually. Then you realize there’s a native HTML element that does the same thing: <details> and <summary>. Zero JavaScript, built-in keyboard navigation, screen reader support out of the box. The catch: the exclusive accordion (name attribute), ::marker styling, CSS animations for open/close, and the toggle/beforetoggle events aren’t well documented in one place.

Why This Reference (Not the HTML Dialog Element Guide)

PureDevTools has an HTML Dialog Element Guide for modal and non-modal dialogs (<dialog>). This reference covers <details> and <summary> — disclosure widgets (collapsible sections, FAQs, accordions). Both are native HTML elements that reduce JavaScript dependency, but they solve different problems: dialogs overlay the page and trap focus; details/summary toggle inline content visibility.

HTML Details & Summary Reference

The <details> and <summary> elements are the browser-native way to build disclosure widgets — collapsible content sections that require zero JavaScript for basic toggle behavior. They power FAQs, accordions, spoilers, and optional content panels with built-in accessibility.

<!-- Minimal disclosure widget -->
<details>
  <summary>What is the details element?</summary>
  <p>A native HTML disclosure widget — collapsible content with no JavaScript needed.</p>
</details>

Open and Close Behavior

The <details> element is a disclosure widget. Clicking the <summary> toggles it between open and closed:

<!-- Closed by default -->
<details>
  <summary>Read more</summary>
  <p>This is shown when opened.</p>
</details>

<!-- Open by default (use the open attribute) -->
<details open>
  <summary>Already expanded</summary>
  <p>Visible immediately on page load.</p>
</details>

Programmatic Control

const details = document.querySelector('details');

// Open
details.open = true;

// Close
details.open = false;

// Check state
console.log(details.open); // true or false

The toggle Event

The toggle event fires after the state changes. Use it to react to open/close without preventing it:

details.addEventListener('toggle', (event) => {
  if (event.target.open) {
    console.log('Opened');
  } else {
    console.log('Closed');
  }
});

// With ToggleEvent (Chrome 114+, Firefox 108+, Safari 17.2+)
details.addEventListener('toggle', (event) => {
  console.log(event.newState); // 'open' or 'closed'
  console.log(event.oldState); // 'open' or 'closed'
});

The beforetoggle Event

The beforetoggle event fires before the state changes and is cancelable:

details.addEventListener('beforetoggle', (event) => {
  // Prevent closing if there are unsaved changes
  if (event.newState === 'closed' && hasUnsavedChanges()) {
    event.preventDefault();
  }
});

beforetoggle requires Chrome 114+, Firefox 130+, Safari 17.2+.

Multiple Details Elements

Without the name attribute, multiple details elements are fully independent — any number can be open simultaneously:

<!-- All independent — multiple can be open at once -->
<details>
  <summary>Section A</summary>
  <p>Content A</p>
</details>

<details>
  <summary>Section B</summary>
  <p>Content B</p>
</details>

Use this pattern when users may want to compare content across multiple sections.

Exclusive Accordion (name Attribute)

The name attribute groups details elements into an exclusive accordion — only one can be open at a time:

<details name="faq">
  <summary>Question 1</summary>
  <p>Answer 1</p>
</details>

<details name="faq">
  <summary>Question 2</summary>
  <p>Answer 2</p>
</details>

<details name="faq">
  <summary>Question 3</summary>
  <p>Answer 3</p>
</details>

When one panel opens, the browser automatically closes the other panels in the group. No JavaScript needed. Supported in Chrome 120+, Firefox 130+, Safari 17.6+.

JavaScript Fallback for Older Browsers

const panels = document.querySelectorAll('details[name="faq"]');
panels.forEach(panel => {
  panel.addEventListener('toggle', () => {
    if (panel.open) {
      panels.forEach(other => {
        if (other !== panel) other.open = false;
      });
    }
  });
});

Styling the Marker (::marker)

The disclosure triangle is a CSS list-item marker. Style it with ::marker:

/* Change marker color */
summary::marker {
  color: #3b82f6;
}

/* Replace marker content */
summary::marker {
  content: '▶ ';
}
details[open] summary::marker {
  content: '▼ ';
}

Custom Indicator with ::before

For full CSS control (transitions, transforms), remove the default marker and use ::before:

/* Remove default triangle */
summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
}
summary::-webkit-details-marker { display: none; } /* Safari */

/* Custom animated arrow */
summary::after {
  content: '▶';
  transition: transform 0.2s ease;
  display: inline-block;
}
details[open] summary::after {
  transform: rotate(90deg);
}

Accessibility

The <summary> element has an implicit ARIA role of button. The browser automatically exposes aria-expanded based on the open state — no manual ARIA needed.

Key Accessibility Requirements

  1. Descriptive summary text: Screen readers announce the summary text as the button label. Use clear, descriptive text.
  2. Keyboard navigation: Enter and Space toggle the details when summary is focused. Tab navigates in/out.
  3. Focus indicators: Never remove outline without a custom focus-visible style.
<!-- Good: descriptive labels -->
<details>
  <summary>Return and refund policy</summary>
  <p>Returns accepted within 30 days of purchase.</p>
</details>

<!-- Add custom focus indicator -->
<style>
  summary:focus-visible {
    outline: 2px solid #3b82f6;
    outline-offset: 2px;
    border-radius: 2px;
  }
</style>

Animating details Open/Close

Modern CSS (Chrome 117+, Firefox 129+, Safari 17.5+)

details > *:not(summary) {
  opacity: 0;
  transform: translateY(-6px);
  transition:
    opacity 0.2s ease,
    transform 0.2s ease,
    display 0.2s allow-discrete;
}

details[open] > *:not(summary) {
  opacity: 1;
  transform: translateY(0);
}

@starting-style {
  details[open] > *:not(summary) {
    opacity: 0;
    transform: translateY(-6px);
  }
}

Classic max-height Approach (All Browsers)

details > *:not(summary) {
  max-height: 0;
  overflow: hidden;
  transition: max-height 0.3s ease;
}
details[open] > *:not(summary) {
  max-height: 500px; /* Must exceed actual content height */
}

Browser Support

FeatureChromeFirefoxSafariEdge
<details> / <summary>1249679
toggle event1249679
name attribute (accordion)12013017.6120
beforetoggle event11413017.2114
::marker on summary866811.186

Common Mistakes

Omitting the summary element: Without <summary>, browsers display a generic ‘Details’ label. Always provide descriptive summary text.

Using ::before to target the triangle: The disclosure triangle is ::marker, not ::before. Set list-style: none to remove it.

Setting display: none on summary: This makes the disclosure widget completely inaccessible — no toggle control is visible or operable.

Blocking the toggle with keydown: Use beforetoggle with event.preventDefault() instead — it cancels both click and keyboard toggle.

FAQ

How do I make only one accordion panel open at a time?

Use the name attribute: <details name="group">. All details elements with the same name form an exclusive group. Supported in Chrome 120+, Firefox 130+, Safari 17.6+.

How do I react to state changes?

Listen to the toggle event on the details element. It fires after every open/close. For modern browsers, the event has newState and oldState properties.

How do I prevent the details from closing?

Use the beforetoggle event and call event.preventDefault(). This cancels the state change before it happens.

Is the details element accessible?

Yes. The summary has an implicit role of ‘button’ with automatic aria-expanded. Screen readers handle it correctly in all major screen reader/browser combinations. Ensure your summary text is descriptive.

How do I animate the open/close?

Use CSS with @starting-style and display allow-discrete for modern browsers (Chrome 117+). For older browsers, use the max-height transition trick.

Related Tools

More HTML & Markdown Tools