Building Accordions with <details> and <summary>
Welcome to the first post in a series where we explore native HTML features that can replace heavy JavaScript libraries. As front-end developers, we often reach for third-party packages to build common UI components. But modern browsers have evolved, and many of these interactive elements are now built right into the DOM.
Today, we're starting simple: the accordion.
Instead of writing custom state management and manually toggling ARIA attributes, we can use the native <details> and <summary> tags to build an accessible, functional accordion with zero JavaScript.
The Bare Bones
At its core, the <details> element represents a disclosure widget from which the user can retrieve additional information. The <summary> element acts as the clickable heading for that widget.
Here is the absolute minimum HTML required:
<details>
<summary>Click to expand</summary>
<p>Here is the hidden content! The browser handles the toggle state and accessibility announcements automatically.</p>
</details>
By default, the browser renders a small triangle next to the summary text. Clicking the summary toggles the open attribute on the <details> tag, showing or hiding the content inside.
The CSS Glow-Up
The default browser styling is functional but rarely fits a modern design system. Fortunately, customizing it is straightforward.
First, we usually want to remove the default triangle marker so we can add our own custom icons (like a plus/minus or a custom chevron).
/* Remove the default triangle and layout the bar */
summary {
list-style: none; /* Works in most modern browsers */
cursor: pointer;
padding: 1rem;
background-color: #f4f4f5;
border-radius: 8px;
font-weight: bold;
/* NEW: Flexbox aligns the text and our new arrow */
display: flex;
justify-content: space-between;
align-items: center;
}
/* Specifically for Safari */
summary::-webkit-details-marker {
display: none;
}
/* NEW: Create the custom arrow */
summary::after {
content: "›"; /* A simple chevron character */
font-size: 1.5rem;
line-height: 1;
/* Add a smooth animation for when it rotates */
transition: transform 0.3s ease;
}
/* Style the content box */
details {
margin-bottom: 1rem;
}
details p {
padding: 1rem;
margin: 0;
border: 2px solid #f4f4f5;
}
Because the browser adds an open attribute to the <details> tag when it's expanded, we can use CSS attribute selectors to style the open state without touching JavaScript.
/* Change the background color when open */
details[open] summary {
background-color: #e4e4e7;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
The Modern Superpower: The name Attribute
Historically, if you wanted an "exclusive" accordion—where opening one section automatically closes the others—you had to write JavaScript to listen for click events and manage the states.
Recently, browsers introduced support for the name attribute on the <details> element. By giving multiple <details> tags the exact same name, you group them together. The browser will natively ensure that only one element in that group is open at a time.
<details name="faq-accordion">
<summary>What is HTML5?</summary>
<p>The latest evolution of the standard that defines HTML.</p>
</details>
<details name="faq-accordion">
<summary>Do I need JavaScript for this?</summary>
<p>Not anymore! The name attribute handles the exclusive toggling natively.</p>
</details>
Why This Matters
Relying on native elements doesn't just reduce your bundle size; it drastically simplifies your codebase. As an added bonus, it makes end-to-end testing incredibly straightforward. If you are writing UI tests with tools like Playwright, you no longer have to wait for custom JS animations or write complex assertions for custom ARIA states—the DOM provides the absolute source of truth.
Interactive Demo:
(Below is a live CodePen demonstrating the exclusive accordion with custom CSS. Feel free to interact with it or tweak the code!)