How to Create an Accordion in CSS
Build a collapsible accordion that reveals one panel of content at a time when its header is clicked.
What an accordion is for
An accordion is a stack of headers, each attached to a panel of content. Clicking a header expands or collapses the panel beneath it - useful whenever you have several chunks of related content (an FAQ, a settings list, a set of steps) and you want the page to stay short until the visitor asks to see more. There are two solid ways to build one: the zero-JavaScript native `<details>`/`<summary>` elements, or a `<button>` + `classList.toggle()` pattern when you need more control over styling and behavior, like closing other panels automatically.
Option 1: the native <details> element
The simplest accordion needs no JavaScript at all. Wrap each section in a `<details>` element and put the clickable header in a `<summary>` child. The browser handles the open/close state, the click handling, and even keyboard access for free.
Example: no-JS accordion with <details>
<!DOCTYPE html>
<html>
<head>
<style>
details {
border: 1px solid #d9dde3;
border-radius: 8px;
margin-bottom: 8px;
padding: 0.5rem 1rem;
}
summary {
cursor: pointer;
font-weight: 600;
list-style: none; /* hide the default triangle marker */
}
summary::-webkit-details-marker {
display: none; /* Safari/Chrome default marker */
}
summary::after {
content: "+";
float: right;
}
details[open] summary::after {
content: "\2212"; /* minus sign when open */
}
</style>
</head>
<body>
<details>
<summary>What is an accordion?</summary>
<p>A UI pattern that hides content behind a clickable header, showing one
or more panels at a time.</p>
</details>
<details>
<summary>Do I need JavaScript?</summary>
<p>No. The <details> element toggles itself. JavaScript is only
needed for extra behavior, like closing other panels automatically.</p>
</details>
</body>
</html>Option 2: a custom accordion with classList.toggle
When you want full control over the animation, icons, or which panels can stay open together, build the accordion from a plain `<button>` header and a sibling content panel. Clicking the button toggles an `is-open` class on itself and on the panel; CSS reacts to that class to show or hide the content and rotate the icon.
Example: custom accordion with JavaScript
<!DOCTYPE html>
<html>
<head>
<style>
.accordion-item { border-bottom: 1px solid #e2e5ea; }
.accordion-header {
width: 100%;
text-align: left;
background: none;
border: none;
padding: 1rem 0;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
}
.accordion-header .icon {
transition: transform 0.2s ease;
}
.accordion-header.is-open .icon {
transform: rotate(45deg); /* turns a "+" into an "x" */
}
.accordion-panel {
max-height: 0;
overflow: hidden;
transition: max-height 0.25s ease;
}
.accordion-panel.is-open {
max-height: 200px; /* large enough to fit the content */
}
.accordion-panel p { margin: 0 0 1rem; }
</style>
</head>
<body>
<div class="accordion-item">
<button class="accordion-header">
Shipping details
<span class="icon">+</span>
</button>
<div class="accordion-panel">
<p>Orders ship within two business days via standard courier.</p>
</div>
</div>
<script>
document.querySelectorAll('.accordion-header').forEach(function (header) {
header.addEventListener('click', function () {
var panel = header.nextElementSibling;
header.classList.toggle('is-open');
panel.classList.toggle('is-open');
});
});
</script>
</body>
</html>The `max-height` trick is what makes the panel animate smoothly: you cannot transition `height: auto`, but you can transition `max-height` from 0 to a value larger than the content will ever need. The panel is clipped with `overflow: hidden` so anything beyond its current max-height stays invisible.
- Use a <button> for the header, not a <div> - buttons are focusable and clickable with the keyboard (Enter/Space) out of the box.
- Toggle a class rather than inline styles, so all the visual states live together in your CSS.
- If you want only one panel open at a time, close every other panel's classes before toggling the clicked one.
- Add aria-expanded="true|false" to the header button and toggle it alongside the class, so screen readers announce whether the panel is currently open.
Exercise: Interactive Components
Which statement about building a dropdown toggle is accurate?
Frequently Asked Questions
- How do I make an accordion without JavaScript?
- Use
<details>with a<summary>heading. The browser handles opening and closing, keyboard access and screen reader semantics for free. Style the marker withsummary::markeror hide it and supply your own icon. - How do I animate an accordion opening?
- Transition
grid-template-rowsfrom0frto1fron a wrapper, or animatemax-heightto a value larger than the content. Height itself cannot be transitioned fromauto, which is why naive height animations do nothing.