Skip to content

Accordion

A set of headed sections that expand and collapse.

import { Accordion } from "@evolve-storefront/ui/accordion";
Design notes

react-aria-components owns the behaviour, which is the whole reason this is a primitive rather than markup: a trigger that is a real <button> inside a heading, aria-expanded on it, and aria-controls pointing at the panel.

A collapsed panel is hidden="until-found", not unmounted — out of the accessibility tree, but still reachable by the browser's find-in-page, which expands the section it lands in. For an FAQ that is the behaviour you want.

Triggers are reached with Tab, which is the disclosure pattern. Arrow-key movement between them is the accordion pattern's optional extra and RAC does not implement it, so neither do we.

Single-open by default — pass allowsMultipleExpanded for a set where more than one answer is useful at a time. Collapsible either way.

Preview

DefaultOpen in Storybook ↗

Usage

Single-open: opening a second section closes the first.

<Accordion>
{sections.map((section) => (
<AccordionItem key={section.id} id={section.id}>
<AccordionTrigger headingLevel={3}>{section.question}</AccordionTrigger>
<AccordionPanel>{section.answer}</AccordionPanel>
</AccordionItem>
))}
</Accordion>

Props

PropTypeDescription
allowsMultipleExpanded?boolean

Let more than one section stand open. Default: one at a time.

defaultExpandedKeys?Iterable<string>

Ids of the sections open on first render.

expandedKeys?Iterable<string>

The open sections, for a caller that owns the state.

onExpandedChange?((keys: Set<string>) => void)

Reports the open set. Pairs with expandedKeys.

Any other props go to the root element, like on a plain HTML element.

Change the layout

Pass children to Accordion to arrange its parts yourself. Every part is exported from @evolve-storefront/ui/accordion.

<Accordion>
{sections.map((section) => (
<AccordionItem key={section.id} id={section.id}>
<AccordionTrigger headingLevel={3}>{section.question}</AccordionTrigger>
<AccordionPanel>{section.answer}</AccordionPanel>
</AccordionItem>
))}
</Accordion>
AccordionItemOne section: an AccordionTrigger and an AccordionPanel.
PropTypeDescription
idstring

Identifies the section in expandedKeys. Required within a group.

Any other props go to the root element, like on a plain HTML element.

AccordionPanelThe section's content.

No props of its own.

AccordionTriggerThe control that opens a section.
PropTypeDescription
headingLevel?HeadingLevel

The level of the heading wrapping the trigger.

More

A heading, not a bare button, because that is what puts the sections in a screen reader's heading list. RAC defaults it to 3, which is right only at one depth — so a caller that knows its depth passes one.

Any other props go to the root element, like on a plain HTML element.