Accordion
The <x-wirekit:: component creates collapsible content sections with single or multiple open modes. It follows the WAI-ARIA Accordion pattern.
- Accordion (this component) — a coordinated group of panels with optional single-open behavior and card chrome. Opening one panel can close the others.
- Collapsible — a single independent show/hide (a "read more", an advanced-options panel), or a stack of them with no coordination.
- FAQ — this accordion, specialized for questions, that also emits FAQPage structured data derived from what it renders. Use it for a real question list instead of hand-building one here.
Usage
.dark class on any ancestor.
Width & Layout
The accordion fills its parent width. Control the width with Tailwind classes directly on the component:
<x-wirekit::accordion class="max-w-lg">
…
</x-wirekit::accordion>
Modes
Single mode
Opening one item closes all others — similar to radio buttons.
Multiple mode
Any combination of items can be open — similar to checkboxes.
With Icons
Use the header named slot to render custom header content with icons:
Variants
The variant prop changes the container chrome. bordered (default) is a
self-contained card; flush drops the outline so the accordion sits inline in
page content (ideal for an FAQ block); separated turns each item into its own
standalone card with a gap between them.
Size
size="lg" gives the trigger roomier padding and a larger title — useful for
marketing pages and touch-first layouts.
Long Titles
When the accordion has a fixed width, long titles wrap naturally while the chevron stays aligned:
Find in Page
A closed item's content stays findable by the browser's own search (Ctrl+F or ⌘+F). When a match lands in a closed item, the browser opens it, scrolls to it and highlights the text, and the item's header reports it as expanded. In single mode, the item that was open closes, the same as when its header is clicked.
Closed content is still out of sight, out of the tab order and hidden from screen readers until it opens. Turn it off for content that must stay closed until someone chooses to open it, such as the answer to a quiz question:
<x-wirekit::accordion :findable="false">
<x-wirekit::accordion.item title="Show the answer">The answer is 42.</x-wirekit::accordion.item>
</x-wirekit::accordion>
This is a progressive enhancement. It works in browsers that support hidden="until-found"; in the rest, a closed item is hidden entirely and the browser search does not look inside it.
A closed until-found panel stays in the layout. It keeps its box, so get and get report it as visible while the screen is drawn correctly and the reader sees nothing. Playwright's own visibility notion is built on the same geometry.
That matters wherever you ask "is this open?" from outside the component:
- In a test, read the
hiddenattribute, not the geometry. A test that measures the box reads every closed item as open, and the number it reports looks plausible enough not to question — that cost a reader half a day of hunting a browser-lane failure that was never there. - In your own styling, put padding and borders on the element inside the panel. On the panel itself they paint as an empty strip under every closed item. This component and collapsible both do exactly that, for exactly this reason.
Animation
A panel eases its height open and shut. Set :animate="false" to have it appear and disappear instantly instead.
<x-wirekit::accordion :animate="false">
The easing reads --transition-wk-duration from the element, so a theme that sets the duration to zero gets no motion without touching this prop. It is skipped entirely under prefers-reduced-motion: reduce — the panel still opens, it just stops sliding to say so.
Props
<x-wirekit::accordion>
| Prop | Type | Default | Description |
|---|---|---|---|
mode |
string |
'single' |
'single' (only one open at a time) or 'multiple' |
variant |
string |
'bordered' |
'bordered' (card with outline), 'flush' (inline, dividers only), or 'separated' (each item its own card) |
size |
string |
'md' |
Row density — 'md' or 'lg' (roomier padding + larger trigger text) |
level |
int |
3 |
Heading level (1–6) each item's trigger is wrapped in. Set it to match the surrounding outline — 2 directly under the page <h1>, 4 inside an <h3> section |
findable |
bool |
true |
Let the browser's find in page search closed items and open the one a match lands in. See Find in Page |
animate |
bool |
true |
Ease a panel's height open and shut. Skipped under prefers-reduced-motion: reduce. See Animation |
scope |
string|null |
null |
Scoped personalization key |
<x-wirekit::accordion.item>
| Prop | Type | Default | Description |
|---|---|---|---|
id |
string|null |
auto-generated | Stable identifier used for Alpine state and ARIA wiring |
title |
string |
'' |
Header text (or use the header named slot) |
scope |
string|null |
null |
Scoped personalization key |
Accessibility
- Each header is a heading containing a
<button>(allows AT heading navigation). The level comes from the accordion'slevelprop and defaults to<h3>— set it so the accordion does not skip a level in the page's outline - Button:
aria-expanded(dynamic),aria-controls(linked to panel) - Panel:
role="region",aria-labelledby(linked to button) - Chevron rotates 180° when open — decorative,
aria-hidden="true" - Arrow keys,
HomeandEndmove focus between the headers and open nothing on the way — a panel opens onEnterorSpace, so a reader can pass through a long accordion without waiting for every panel to reveal itself - Those keys apply while focus is on a header. Inside an open panel they belong to the content,
so
Homestill moves the caret to the start of a line in a field you put there
Keyboard Interaction
| Key | Action |
|---|---|
Tab |
Move focus to the next accordion header |
Shift+Tab |
Move focus to the previous accordion header |
Enter / Space |
Toggle the focused panel |
ArrowDown / ArrowUp |
Move between accordion headers |
Home / End |
Move to the first / last header |
Pitfalls
- Don't nest accordions more than two levels deep. WAI-ARIA Authoring Practices warn that screen-reader users lose track of the hierarchy. Reach for
<x-wirekit::for genuinely nested data.tree-view> - Don't auto-expand all panels on load. Defeats the disclosure pattern — if everything is open, the accordion is just a stack of headings. Open at most one initial panel.
Design Tokens
| Token | Used for |
|---|---|
--color-wk-bg-elevated |
Accordion container background |
--color-wk-border |
Outer border + per-item separator |
--border-wk-width |
Border width |
--radius-wk-lg |
Container border radius |
Config Defaults
The defaults live in config/ under components.. Override them globally:
'components' => [
'accordion' => [
'mode' => 'multiple', // allow multiple items open by default
'findable' => false, // keep closed items out of the browser's find in page
'animate' => false, // panels appear instantly instead of easing their height
],
],
Further Reading
- WAI-ARIA Accordion Pattern — the authoring pattern this component implements
- MDN:
aria-expanded - MDN:
role="region" - HTML
<details>vs ARIA Accordion — when to choose each