Accordion
stimeo--accordion
Collapsible sections that toggle independently, with header focus navigation.
The stimeo--accordion controller implements the WAI-ARIA Accordion pattern. Each header button is associated with its panel through aria-controls, and multiple panels may be open at once. State is reflected through aria-expanded on the header and the hidden attribute on the panel. The library provides behavior only — styling is owned by this Playground.
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. |