Accordion
stimeo--accordion
Collapsible sections that open independently, with the arrow keys moving between the headers.
Sections that open when you press their header. It implements the WAI-ARIA Accordion pattern, and several sections can stay open at once. Which header is currently open is announced to screen readers. The arrow keys move between the headers.
Ships within 2–3 business days.
Free returns within 30 days.
Covered by a 1-year limited warranty.
Keyboard
| Key | Action |
|---|---|
| Enter / Space | Toggle the focused section (native button). |
| ↓ / ↑ | Move focus between headers (wrapping). |
| Home / End | Focus the first / last header. |
<%# Markup for the accordion (APG Accordion) demo.
stimeo--accordion manages each header's aria-expanded and each panel's hidden,
and allows multiple panels open at once. Arrow keys move focus between headers.
Headers and panels are linked via aria-controls (= the panel's id), and each
panel (role="region") references its header button's id via aria-labelledby. %>
<div class="accordion" data-controller="stimeo--accordion">
<h3 class="accordion__header">
<button type="button" id="acc-shipping-trigger" class="accordion__trigger"
aria-expanded="false" aria-controls="acc-shipping"
data-stimeo--accordion-target="trigger"
data-action="click->stimeo--accordion#toggle
keydown->stimeo--accordion#onKeydown"><%= t(
"components.accordion.demo.shipping_title"
) %></button>
</h3>
<div class="accordion__panel" id="acc-shipping" role="region"
aria-labelledby="acc-shipping-trigger"
data-stimeo--accordion-target="panel" hidden>
<p><%= t("components.accordion.demo.shipping_desc") %></p>
</div>
<h3 class="accordion__header">
<button type="button" id="acc-returns-trigger" class="accordion__trigger"
aria-expanded="false" aria-controls="acc-returns"
data-stimeo--accordion-target="trigger"
data-action="click->stimeo--accordion#toggle
keydown->stimeo--accordion#onKeydown"><%= t(
"components.accordion.demo.returns_title"
) %></button>
</h3>
<div class="accordion__panel" id="acc-returns" role="region"
aria-labelledby="acc-returns-trigger"
data-stimeo--accordion-target="panel" hidden>
<p><%= t("components.accordion.demo.returns_desc") %></p>
</div>
<h3 class="accordion__header">
<button type="button" id="acc-warranty-trigger" class="accordion__trigger"
aria-expanded="false" aria-controls="acc-warranty"
data-stimeo--accordion-target="trigger"
data-action="click->stimeo--accordion#toggle
keydown->stimeo--accordion#onKeydown"><%= t(
"components.accordion.demo.warranty_title"
) %></button>
</h3>
<div class="accordion__panel" id="acc-warranty" role="region"
aria-labelledby="acc-warranty-trigger"
data-stimeo--accordion-target="panel" hidden>
<p><%= t("components.accordion.demo.warranty_desc") %></p>
</div>
</div>
/*
* Presentation-only styles for the accordion demo.
* Open/closed state is expressed via aria-expanded (set by the library).
*/
.accordion {
width: min(28rem, 100%);
border: 1px solid var(--border);
border-radius: 0.375rem;
}
.accordion__header {
margin: 0;
}
.accordion__trigger {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 0.75rem 1rem;
font-size: 1rem;
text-align: left;
cursor: pointer;
background: var(--surface-card);
border: 0;
border-bottom: 1px solid var(--border);
}
/* Show "−" on open headers and "+" on closed ones. */
.accordion__trigger::after {
content: "+";
color: var(--muted);
}
.accordion__trigger[aria-expanded="true"]::after {
content: "\2212";
}
.accordion__panel {
padding: 0.75rem 1rem;
border-bottom: 1px solid var(--border);
}
.accordion__panel:last-child,
.accordion__header:last-of-type .accordion__trigger {
border-bottom: 0;
}
This demo needs no consumer-side JS (the controller handles the behavior).
These demo styles use shared design tokens (light + dark). Copy the shared styles too, then toggle data-theme on your root element for dark mode.
The data-* attributes you add to your own HTML to wire this component. Put the data-controller below on a root element, then place its targets / values / actions inside that element.
On the root element
data-controller="stimeo--accordion"
Targets
| Name | Description | Attribute |
|---|---|---|
trigger
required
|
A header button that toggles its associated panel, paired to it via aria-controls. |
data-stimeo--accordion-target="trigger" |
panel
required
|
A collapsible region whose id is referenced by a trigger's aria-controls; its hidden attribute reflects state. |
data-stimeo--accordion-target="panel" |
Actions
| Name | Description | Action |
|---|---|---|
collapseAll
|
Closes every panel at once; bind to an optional "collapse all" control. | stimeo--accordion#collapseAll |
expandAll
|
Opens every panel at once; bind to an optional "expand all" control. | stimeo--accordion#expandAll |
onKeydown
|
Moves focus between headers: ArrowDown/Up cycle, Home/End jump to first/last. | stimeo--accordion#onKeydown |
toggle
|
Toggles the activated header's panel and syncs aria-expanded and the panel's hidden attribute. |
stimeo--accordion#toggle |
State hooks
The library only manages these ARIA/data attributes and custom properties. Your CSS reads them to render the look — selectors like [aria-selected], [aria-expanded], or var(--stimeo--…) hook into this state.
| Hook | Target | Meaning |
|---|---|---|
aria-expanded |
Each header | "true" when its panel is open, "false" when collapsed. |
hidden |
Each panel | Removed when open, present when collapsed. |