Mobile navigation

View all posts

Learn how to build an expandable and collapsible component using the native HTML details tag

Hey! This is Mica writing, a human, not an AI agent (nothing against them, no discrimination of any kind is allowed here). If you are a human, let's connect, at the end you will find the ways to reach me. Before starting, I have a question: Is there anybody out there?

Introduction

Welcome to a new entry in my section dedicated to creating reusable and, above all, accessible components. This time, we are going to learn how to use the HTML <details> tag to create a disclosure component without the need for JavaScript or WAI-ARIA.

Requirements to have a functional disclosure

To make a disclosure accessible, a few requirements must be met:

  • When the focus is on the disclosure element header (<summary>), it must be possible to collapse and expand it by pressing the Enter or Space keys.
  • When pressing Tab, the focus moves to the next interactive element inside the disclosure element or, if there are none, to the next interactive element on the page.
  • When pressing Shift + Tab, the focus moves to the previous interactive element.
  • The screen reader must announce the state of the element, that is, whether it is collapsed or expanded. It must also announce its accessible name.

The <details> tag will help us meet all these points, since it is natively accessible.

Anatomy of the details tag

<details>

The main element that wraps the whole disclosure element.

<summary>

First direct child of the <details> tag. It is used as a header acting as the control to expand or collapse the content. There is no need to group the content of the summary inside another tag, but the following tags are valid to use: <h1>-<h6> and phrasing content.

::marker

The disclosure triangle that indicates whether the element is open or closed.

::details-content

Direct child of the <details> tag and sibling of <summary> and it can be any valid HTML element. The best part is that there is no need to wrap the content inside a div or a fragment, natively the <details> tag will detect it as a content.

Attributes

open

This attribute indicates whether the details content is expanded or collapsed.

  • This attribute is implicit on the tag, so there is no need to add it because the details tag is collapsed by default.
  • If a plain open is added, the details will be expanded by default.
name

This attribute will only be necessary when it is needed to manage more than one details element as a group. In other words, if one details is open and another is opened, the previously open one will automatically close. If you want to play a little bit with this attribute, you can go to this use case.

CSS Strategies to Style The Disclosure Component

How to style summary

Natively, the <summary> tag comes by default with the property display: list-item. This property gives us the native the disclosure icon, which can be a triangle, a circle, only the disclosure-open or the disclosure-close icon, or use the CSS at-rule @counter-style to set a predefined list of icon styles. On the other hand, with the pseudo-element ::marker, the disclosure icon can be styled:

::marker –– Code Snippet

::marker {
  color: orangered;
  font-size: 2rem;
}

::marker –– Functional Example

Styled disclosure icon

The pseudo-element ::marker lets us restyle the native disclosure icon.

Cool, but now, how can the native disclosure icon be omitted? Since the <summary> tag comes by default with the property display: list-item, by simply adding a display: flex on the details > summary selector, the native disclosure icon is hidden automatically. This strategy is valid, but it is necessary to have a visual symbol that makes it explicit that this is a disclosure element. The added icon should be aria-hidden="true" because assistive technology users will already perceive the role and the state of the element.

Removing Native Disclosure Icon –– Code Snippet

details > summary {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
}

Removing Native Disclosure Icon –– Functional Example

Hidden disclosure icon

As we explained above, the native icon is hidden because of the display: flex, and a decorative icon is provided.

Finally, I would like to comment that there is also another well-known pseudo-element named ::-webkit-details-marker that is used in WebKit-based browsers to style details marker. To be honest, that pseudo-selector today is not really necessary since ::marker is well supported across different browsers.

Bye, bye div to generate content!

Since September 2025, we have the CSS pseudo-selector ::details-content that auto-generates a wrapper box to style the expandable/collapsible content of the <details> tag. This is good news because now we do not need to rely on a non-semantic tag, like a div, to style the wrapper of the content and to handle the transitions from the collapsed state to the expanded and vice versa.

Expandable/Collapsible Content –– CSS Snippet

details::details-content {
  block-size: 0;
  overflow: clip;
}

details[open]::details-content {
  padding: 0.85rem;
}

@media (prefers-reduced-motion: no-preference) {
  details::details-content {
    transition:
      block-size 0.3s ease,
      padding-block 0.3s ease,
      content-visibility 0.3s ease allow-discrete;
  }
}
  • details::details-content, holds the styles for the collapsed default state and hides the content.
  • details[open]::details-content, holds the styles for the expanded state. It will determine the end of the animation, if there is any.
  • @media (prefers-reduced-motion: no-preference) {details::details-content {}}, this is an accessibility improvement! Here is where the animation between the closed and open states is going to live if the user has animations enabled in their operating system.

Expandable/Collapsible Content –– Functional Example

Styled collapsible content

Everything in this panel lives inside ::details-content, the native wrapper around a <details>'s collapsible body. Because it is a real box, you can style it like any other element.

What you can style

  • Its own background, padding, and border.
  • Typography: headings, emphasis, and inline code.
  • The open/close transition, with zero JavaScript.

Read more about ::details-content on MDN.

A correct semantic structure would be rendered as the following block of code, and you will not find any non-semantic HTML element:

<details>
  <summary>{...}</summary>
  {here starts the ::details-content}
  <p>{...}</p>
  <h4>{...}</h4>
  <ul>
    <li>{...}</li>
    <li>{...}</li>
    <li>{...}</li>
  </ul>
</details>

Keyboard Navigation

The element that receives focus and manages the interaction is summary
Chrome DevTools console with the selector document.activeElement pointing that the element summary is the one that receives the focus and manages the expand and collapse action.
Chrome DevTools console with the selector document.activeElement pointing that the element summary is the one that receives the focus and manages the expand and collapse action.
KeyAction
TabMove focus to the disclosure element or move focus to the next disclosure element
Shift + TabMove focus to the previous disclosure element or move focus to the previous interactive element
Space or EnterExpands/collapses the details element

Screen Readers

Here is how different combinations of browsers, devices and screen readers announce and interact with a disclosure element. Interact with the disclosure element below to discover the answers:

How do screen readers announce the details tag?
  • VoiceOver macOS Tahoe 26.5.2 + Safari 26.5.2 (21624.2.5.11.8):
    • Collapsed: 'How do screen readers announce the details tag?, collapsed, summary'
    • Expanded: 'How do screen readers announce the details tag?, expanded, summary'
    • Toggle state with keys combination Control + Option + Space: 'collapsed' or 'expanded'
  • VoiceOver macOS Tahoe 26.5.2 + Chrome 150.0.7871.184:
    • Collapsed: 'How do screen readers announce the details tag?, collapsed, disclosure triangle, group'
    • Expanded: 'How do screen readers announce the details tag?, expanded, disclosure triangle, group'
    • Toggle state with keys combination Control + Option + Space: if it is collapsed, the whole thing again is announced as collapsed and if it is expanded, the whole thing again is announced as expanded
  • VoiceOver macOS Tahoe 26.5.2 + Firefox 152.0.6:
    • same as in the combination VoiceOver macOS Tahoe 26.5.2 + Chrome 150.0.7871.184
  • NVDA 2026.1.1 + Chrome 150.0.7871.18:
    • Collapsed: 'How do screen readers announce the details tag?, button, collapsed'
    • Expanded: 'How do screen readers announce the details tag?, button, expanded'
    • Toggle state with Enter or Space: 'collapsed' or 'expanded'
  • NVDA 2026.1.1 + Firefox 153.0:
    • Collapsed: 'How do screen readers announce the details tag?, button, collapsed'
    • Expanded: 'How do screen readers announce the details tag?, button, expanded'
    • Toggle state with Enter or Space: 'collapsed' or 'expanded'
  • TalkBack, Pixel 10, Android 16 + Chrome 149.0.7827.160:
    • Collapsed: 'collapsed, How do screen readers announce the details tag?, disclosure triangle'
    • Expanded: 'expanded, How do screen readers announce the details tag?, disclosure triangle'
    • Toggle state by double-tapping to activate: 'collapsed' or 'expanded'
  • TalkBack, Pixel 10, Android 16 + Firefox 145.0.2:
    • Collapsed: 'collapsed, How do screen readers announce the details tag?, button, How do screen readers announce the details tag?, Space'
    • Expanded: 'expanded, How do screen readers announce the details tag?, button, How do screen readers announce the details tag?, Space'
    • Toggle state by double-tapping to activate: 'collapsed' or 'expanded'

Use cases

The same element powers very different UI. Here are a few patterns built with nothing but its native tags, the name attribute and the ::details-content pseudo-element, still with zero JavaScript.

  1. FAQ section

    One answer open at a time. The same name attribute on every panel makes the browser close the others for you.

    Do I need JavaScript to handle the interactions in an accordion/disclosure element?

    No. <details> handles the open and closed state, keyboard operation, and screen-reader announcements on its own.

    How do I keep only one panel open at a time?

    Give each <details> the same name. This very list uses name="faq", so opening a question closes the previous one.

    Can the summary contain a heading?

    Yes. A <summary> may wrap an <h2><h6> or any phrasing content, so your FAQ can stay part of the document outline.

  2. Inline "read more"

    Keep the intro, hide the detail. display: inline on the details and ::details-content folds it into the sentence.

    En un lugar de la Mancha, de cuyo nombre no quiero acordarme, no ha mucho tiempo que vivía un hidalgo de los de lanza en astillero, adarga antigua, rocín flaco y galgo corredor.
    Read moreRead less Una olla de algo más vaca que carnero, salpicón las más noches, duelos y quebrantos los sábados, lentejas los viernes, algún palomino de añadidura los domingos, consumían las tres partes de su hacienda. Don Quijote de la Mancha, Miguel de Cervantes, 1605.
  3. Spoiler or show solution

    Hide an answer, a code solution, or a plot spoiler behind an explicit reveal. Closed, the content stays out of view and out of the accessibility tree.

    How to sum an array of numbers with JavaScript?

    Show solutionHide solution

    const total = nums.reduce((a, b) => a + b, 0). It walks the array once and calculates the total.

  4. Show order details

    Keep a card calm by default and tuck the specifics behind a toggle. Perfect for order metadata, technical specs, or anything power-users want but everyone else can skip.

    Order #2048

    Shipped. Arrives Thu 15 Aug

    $38.00

    View order details
    Seed box 1
    $32.00
    Fast shipping
    $6.00
    Carrier
    Tracked 48
    Tracking
    00-0000-00
  5. Animated markers

    The little indicator carries all the feedback. Because the open/close state is a plain CSS selector, you can animate any marker you like — a chevron that spins, a dot that blooms into a ring.

    Not grouped: every card opens and closes on its own.

    • Expanding dot

      A filled dot expands into a hollow ring when the panel opens.

    • Spinning chevron

      A sideways arrow rotates a quarter-turn to point down.

    • Plus to minus

      The vertical stroke of a plus rotates away, leaving a minus.

  6. Collapsible filter panel

    Group form controls under collapsible headers to keep a long filter rail scannable. Each group remembers its own state; the inputs inside work exactly as they would anywhere else.

    Price
    Difficulty
  7. Specifications

    Keep the buy box clean and file the full spec sheet behind a toggle. Shoppers who need dimensions, materials, or care instructions open it; everyone else skips straight to checkout.

    Full specifications
    Box dimensions
    24 × 18 × 6 cm
    Weight
    640 g
    Seed varieties
    6 heirloom, non-GMO
    Packaging
    100% compostable
    Shelf life
    Sow within 18 months
  8. Collapsed code and logs

    The GitHub pattern: fold a long code sample, diff, or stack trace behind a summary so issues and docs stay scannable. The revealed block is a monospace panel.

    index.html
    4 added
    1. <details>
    2. <summary>Show more</summary>
    3. <p>Everything before or after the summary is part of the content.</p>
    4. </details>
    This use case uses the tag <ins> for the insertion of the new lines. You can read more about this tag its use here
  9. Emoji marker

    A playful take on the animated marker: emoji as custom markers that change based on the open/closed state, perfect for thematic content.

    Does it bloom?

    The seedling emoji marks the collapsed state and sunflower emoji marks the expanded state.