Accordion
A Tailwind CSS accordion component for organizing content into collapsible sections.
<div class="accordion max-w-lg">
<div class="accordion-item">
<button
class="accordion-trigger"
type="button"
data-sp-toggle="accordion"
data-sp-target="#acc-1-panel"
aria-expanded="true"
aria-controls="acc-1-panel"
>
Is it accessible?
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="[[aria-expanded=true]>&]:rotate-180"><path d="m6 9 6 6 6-6"/></svg>
</button>
<div id="acc-1-panel" class="accordion-panel open">
<div class="accordion-content">
Yes. It adheres to the WAI-ARIA design pattern for accordions.
</div>
</div>
</div>
<div class="accordion-item">
<button
class="accordion-trigger"
type="button"
data-sp-toggle="accordion"
data-sp-target="#acc-2-panel"
aria-expanded="false"
aria-controls="acc-2-panel"
>
Is it styled?
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="[[aria-expanded=true]>&]:rotate-180"><path d="m6 9 6 6 6-6"/></svg>
</button>
<div id="acc-2-panel" class="accordion-panel">
<div class="accordion-content">
Yes. It comes with default styles that match your theme.
</div>
</div>
</div>
<div class="accordion-item">
<button
class="accordion-trigger"
type="button"
data-sp-toggle="accordion"
data-sp-target="#acc-3-panel"
aria-expanded="false"
aria-controls="acc-3-panel"
>
Is it animated?
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="[[aria-expanded=true]>&]:rotate-180"><path d="m6 9 6 6 6-6"/></svg>
</button>
<div id="acc-3-panel" class="accordion-panel">
<div class="accordion-content">
Yes. It is animated using CSS animations.
</div>
</div>
</div>
</div>Multiple open
Add data-sp-multiple to the accordion to allow multiple sections to be open at the same time.
<div class="accordion max-w-lg" data-sp-multiple="">
<div class="accordion-item">
<button
class="accordion-trigger"
type="button"
data-sp-toggle="accordion"
data-sp-target="#multi-1-panel"
aria-expanded="true"
aria-controls="multi-1-panel"
>
First section
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="[[aria-expanded=true]>&]:rotate-180"><path d="m6 9 6 6 6-6"/></svg>
</button>
<div id="multi-1-panel" class="accordion-panel open">
<div class="accordion-content">
This accordion allows multiple sections to be open at once.
</div>
</div>
</div>
<div class="accordion-item">
<button
class="accordion-trigger"
type="button"
data-sp-toggle="accordion"
data-sp-target="#multi-2-panel"
aria-expanded="true"
aria-controls="multi-2-panel"
>
Second section
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="[[aria-expanded=true]>&]:rotate-180"><path d="m6 9 6 6 6-6"/></svg>
</button>
<div id="multi-2-panel" class="accordion-panel open">
<div class="accordion-content">
Both sections can be expanded simultaneously.
</div>
</div>
</div>
</div>Icons
Add any SVG icon inside the trigger. Use aria-expanded selectors to control icon behavior.
<div class="accordion max-w-lg">
<div class="accordion-item">
<button
class="accordion-trigger"
type="button"
data-sp-toggle="accordion"
data-sp-target="#icon-1-panel"
aria-expanded="true"
aria-controls="icon-1-panel"
>
Rotating chevron
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="[[aria-expanded=true]>&]:rotate-180"><path d="m6 9 6 6 6-6"/></svg>
</button>
<div id="icon-1-panel" class="accordion-panel open">
<div class="accordion-content">
The chevron rotates 180° when expanded.
</div>
</div>
</div>
<div class="accordion-item">
<button
class="accordion-trigger"
type="button"
data-sp-toggle="accordion"
data-sp-target="#icon-2-panel"
aria-expanded="false"
aria-controls="icon-2-panel"
>
Plus/minus swap
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="[[aria-expanded=true]>&]:hidden"><path d="M5 12h14"/><path d="M12 5v14"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="hidden [[aria-expanded=true]>&]:block"><path d="M5 12h14"/></svg>
</button>
<div id="icon-2-panel" class="accordion-panel">
<div class="accordion-content">Swap between plus and minus icons.</div>
</div>
</div>
<div class="accordion-item">
<button
class="accordion-trigger"
type="button"
data-sp-toggle="accordion"
data-sp-target="#icon-3-panel"
aria-expanded="false"
aria-controls="icon-3-panel"
>
No icon
</button>
<div id="icon-3-panel" class="accordion-panel">
<div class="accordion-content">You can also omit the icon entirely.</div>
</div>
</div>
</div>Disabled
Add disabled to the trigger button to prevent interaction.
<div class="accordion max-w-lg">
<div class="accordion-item">
<button
class="accordion-trigger"
type="button"
data-sp-toggle="accordion"
data-sp-target="#dis-1-panel"
aria-expanded="true"
aria-controls="dis-1-panel"
>
Enabled section
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="[[aria-expanded=true]>&]:rotate-180"><path d="m6 9 6 6 6-6"/></svg>
</button>
<div id="dis-1-panel" class="accordion-panel open">
<div class="accordion-content">This section can be toggled.</div>
</div>
</div>
<div class="accordion-item">
<button
class="accordion-trigger"
type="button"
disabled=""
data-sp-toggle="accordion"
data-sp-target="#dis-2-panel"
aria-expanded="false"
aria-controls="dis-2-panel"
>
Disabled section
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="[[aria-expanded=true]>&]:rotate-180"><path d="m6 9 6 6 6-6"/></svg>
</button>
<div id="dis-2-panel" class="accordion-panel">
<div class="accordion-content">This section cannot be opened.</div>
</div>
</div>
<div class="accordion-item">
<button
class="accordion-trigger"
type="button"
data-sp-toggle="accordion"
data-sp-target="#dis-3-panel"
aria-expanded="false"
aria-controls="dis-3-panel"
>
Another enabled section
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="[[aria-expanded=true]>&]:rotate-180"><path d="m6 9 6 6 6-6"/></svg>
</button>
<div id="dis-3-panel" class="accordion-panel">
<div class="accordion-content">This section can also be toggled.</div>
</div>
</div>
</div>How it works
The accordion component uses a small JavaScript module that handles opening and closing content panels. By default, only one section can be open at a time.
Structure
An accordion consists of five parts:
.accordion- Container that groups accordion items.accordion-item- Individual collapsible section.accordion-trigger- Button that toggles the section.accordion-panel- Panel wrapper that controls visibility and animations.accordion-content- Content container with padding and text styles
<div class="accordion">
<div class="accordion-item">
<button
class="accordion-trigger"
data-sp-toggle="accordion"
data-sp-target="#panel-1"
>
Section title
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="[[aria-expanded=true]>&]:rotate-180"><path d="m6 9 6 6 6-6"/></svg>
</button>
<div id="panel-1" class="accordion-panel">
<div class="accordion-content">Section content</div>
</div>
</div>
</div>Opening and closing
Use data attributes to control the accordion without writing JavaScript.
Add data-sp-toggle="accordion" to the trigger button and data-sp-target to specify which panel to toggle:
<button data-sp-toggle="accordion" data-sp-target="#panel-1">Toggle</button>By default, opening a panel will close any other open panels in the same accordion. Add data-sp-multiple to the .accordion container to allow multiple panels open at once.
For programmatic control, use the global sp.accordion module:
const panel = document.querySelector("#panel-1");
sp.accordion.open(panel);
sp.accordion.close(panel);
sp.accordion.toggle(panel);Animation
The accordion includes a smooth height animation using CSS keyframes. Icons inside the trigger also animate by default.
To disable all animations, add no-animation to the trigger:
<button class="accordion-trigger no-animation">
Section title
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="[[aria-expanded=true]>&]:rotate-180"><path d="m6 9 6 6 6-6"/></svg>
</button>
<div class="accordion-panel no-animation">...</div>To animate icons, use aria-expanded selectors. Icons have a default transition that matches the height animation:
<!-- Rotate icon -->
<svg class="[[aria-expanded=true]>&]:rotate-180" ...></svg>
<!-- Swap icons -->
<svg class="[[aria-expanded=true]>&]:hidden" ...></svg>
<svg class="hidden [[aria-expanded=true]>&]:block" ...></svg>Accessibility
For proper accessibility, add aria-expanded and aria-controls to the trigger button:
<button
data-sp-toggle="accordion"
data-sp-target="#panel-1"
aria-expanded="false"
aria-controls="panel-1"
>
Toggle
</button>The JavaScript will automatically update aria-expanded when the panel opens and closes.
Keyboard navigation
| Key | Action |
|---|---|
Enter / Space | Opens/closes the panel when trigger is focused |
ArrowDown | Move focus to next trigger |
ArrowUp | Move focus to previous trigger |
Home | Move focus to first trigger |
End | Move focus to last trigger |
Tab | Move focus to next/previous focusable element |
Class reference
| Class | Description |
|---|---|
accordion | Container that groups accordion items |
accordion-item | Individual collapsible section |
accordion-trigger | Button that toggles the section |
accordion-panel | Panel wrapper that controls visibility and animations |
accordion-content | Content container with padding and text styles |
open | Add to .accordion-panel to show by default |
no-animation | Add to .accordion-trigger or .accordion-panel to disable animations |
Data attributes
| Attribute | Description |
|---|---|
data-sp-toggle="accordion" | Add to trigger button to toggle the accordion panel |
data-sp-target="#id" | Add to trigger button to specify which panel to toggle |
data-sp-multiple | Add to .accordion to allow multiple panels open at once |
data-state | Set on .accordion-panel to open or closed during animation |