Accordion

A Tailwind CSS accordion component for organizing content into collapsible sections.

Yes. It adheres to the WAI-ARIA design pattern for accordions.
Yes. It comes with default styles that match your theme.
Yes. It is animated using CSS animations.
<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.

This accordion allows multiple sections to be open at once.
Both sections can be expanded simultaneously.
<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.

The chevron rotates 180° when expanded.
Swap between plus and minus icons.
You can also omit the icon entirely.
<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.

This section can be toggled.
This section cannot be opened.
This section can also be toggled.
<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:

  1. .accordion - Container that groups accordion items
  2. .accordion-item - Individual collapsible section
  3. .accordion-trigger - Button that toggles the section
  4. .accordion-panel - Panel wrapper that controls visibility and animations
  5. .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

KeyAction
Enter / SpaceOpens/closes the panel when trigger is focused
ArrowDownMove focus to next trigger
ArrowUpMove focus to previous trigger
HomeMove focus to first trigger
EndMove focus to last trigger
TabMove focus to next/previous focusable element

Class reference

ClassDescription
accordionContainer that groups accordion items
accordion-itemIndividual collapsible section
accordion-triggerButton that toggles the section
accordion-panelPanel wrapper that controls visibility and animations
accordion-contentContent container with padding and text styles
openAdd to .accordion-panel to show by default
no-animationAdd to .accordion-trigger or .accordion-panel to disable animations

Data attributes

AttributeDescription
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-multipleAdd to .accordion to allow multiple panels open at once
data-stateSet on .accordion-panel to open or closed during animation