Live code
Storybook
Usage
Accordion
Usage
Default (Stay Open)
import React, { useState } from 'react';
import { Accordion, AccordionItem, AccordionHeader, AccordionContent } from '@alma-oss/spirit-web-react';
import { type AccordionOpenStateType } from '@alma-oss/spirit-web-react/types';
export const Example = () => {
const [openState, setOpenState] = useState<AccordionOpenStateType>(undefined);
const toggle = (id) => {
if (Array.isArray(openState)) {
if (openState.includes(id)) {
setOpenState(openState.filter((accordionId) => accordionId !== id));
} else {
setOpenState([...openState, id]);
}
} else if (openState === id) {
setOpenState(undefined);
} else {
setOpenState(id);
}
};
return (
<Accordion open={openState} toggle={toggle}>
<AccordionItem id="accordion-item-example-0">
<AccordionHeader>Accordion Header</AccordionHeader>
<AccordionContent>Accordion Content</AccordionContent>
</AccordionItem>
<AccordionItem id="accordion-item-example-1">
<AccordionHeader>Accordion Header</AccordionHeader>
<AccordionContent>Accordion Content</AccordionContent>
</AccordionItem>
<AccordionItem id="accordion-item-example-2">
<AccordionHeader>Accordion Header</AccordionHeader>
<AccordionContent>Accordion Content</AccordionContent>
</AccordionItem>
<AccordionItem id="accordion-item-example-3">
<AccordionHeader>Accordion Header</AccordionHeader>
<AccordionContent>Accordion Content</AccordionContent>
</AccordionItem>
</Accordion>
);
};
Default with Opened on Init
const [openState, setOpenState] = useState<AccordionOpenStateType>(['accordion-item-example-1']);
Open Only One at a Time
const [openState, setOpenState] = useState<AccordionOpenStateType>('');
Uncontrolled Accordion (Stay Open)
import { UncontrolledAccordion, AccordionItem, AccordionHeader, AccordionContent } from '@alma-oss/spirit-web-react';
import { AccordionOpenStateType } from '@alma-oss/spirit-web-react/types';
export const Example = () => {
return (
<UncontrolledAccordion stayOpen>
<AccordionItem id="accordion-item-example-0">
<AccordionHeader>Accordion Header</AccordionHeader>
<AccordionContent>Accordion Content</AccordionContent>
</AccordionItem>
<AccordionItem id="accordion-item-example-1">
<AccordionHeader>Accordion Header</AccordionHeader>
<AccordionContent>Accordion Content</AccordionContent>
</AccordionItem>
</UncontrolledAccordion>
);
};
Uncontrolled Accordion with Default Open Value (Stay Open)
<UncontrolledAccordion defaultOpen={['accordion-item-example-1']} stayOpen>
<AccordionItem id="accordion-item-example-0">
<AccordionHeader>Accordion Header</AccordionHeader>
<AccordionContent>Accordion Content</AccordionContent>
</AccordionItem>
<AccordionItem id="accordion-item-example-1">
<AccordionHeader>Accordion Header</AccordionHeader>
<AccordionContent>Accordion Content</AccordionContent>
</AccordionItem>
</UncontrolledAccordion>
Uncontrolled Accordion with Open Only One at a Time
<UncontrolledAccordion>
<AccordionItem id="accordion-item-example-0">
<AccordionHeader>Accordion Header</AccordionHeader>
<AccordionContent>Accordion Content</AccordionContent>
</AccordionItem>
<AccordionItem id="accordion-item-example-1">
<AccordionHeader>Accordion Header</AccordionHeader>
<AccordionContent>Accordion Content</AccordionContent>
</AccordionItem>
</UncontrolledAccordion>
Uncontrolled Accordion with Open Only One at a Time and Default Open Value
<UncontrolledAccordion defaultOpen="accordion-item-example-1">
<AccordionItem id="accordion-item-example-0">
<AccordionHeader>Accordion Header</AccordionHeader>
<AccordionContent>Accordion Content</AccordionContent>
</AccordionItem>
<AccordionItem id="accordion-item-example-1">
<AccordionHeader>Accordion Header</AccordionHeader>
<AccordionContent>Accordion Content</AccordionContent>
</AccordionItem>
</UncontrolledAccordion>
Accordion Wrapper
The Accordion component defaults to using a <section> element, but you can customize it using the elementType prop:
Default (section):
<Accordion>{/* items */}</Accordion>
For semantic lists, use ul or ol:
<Accordion elementType="ul">{/* items */}</Accordion>
⚠️ If you don't know the content or already know there will be a nested list, use div with role="list" instead. This avoids nested ul/li structures (e.g., when accordion items use li elements and the content inside also contains a ul list), which would require CSS resets. The role="list" attribute is essential for accessibility: it tells screen readers and other assistive technologies that this is a list structure, preserving the semantic meaning that would otherwise be lost when using a div instead of a semantic ul or ol element:
<Accordion elementType="div" role="list">
{/* items */}
</Accordion>
AccordionItem
The AccordionItem component defaults to using an <article> element, but you can customize it using the elementType prop:
Default (article):
<AccordionItem id="item-1">{/* header and content */}</AccordionItem>
For semantic lists, use li:
<AccordionItem id="item-1" elementType="li">
{/* header and content */}
</AccordionItem>
⚠️ When using div with role="list" for the Accordion wrapper, use div with role="listitem" for items:
<AccordionItem id="item-1" elementType="div" role="listitem">
{/* header and content */}
</AccordionItem>
AccordionHeader
The AccordionHeader component defaults to using an <h3> element, but you can customize it using the elementType prop to match your document structure (e.g., h2, h4, etc.):
<AccordionHeader elementType="h2">Accordion Header</AccordionHeader>
Accordion Props
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
| children | ReactNode | — | ✓ | Accordion children's nodes |
| elementType | ElementType | section | ✕ | Type of element used as wrapper |
| open | [string | string[]] | — | ✕ | Open item or list of open items * |
| toggle | (id: string) => void | — | ✕ | A generic handler for a single AccordionItem |
(*) Depending on the type of default value, what is set as the default will affect whether one or more will be open at the same time.
On top of the API options, the components accept additional attributes. If you need more control over the styling of a component, you can use style props and escape hatches.
Uncontrolled Accordion Props
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
| children | ReactNode | — | ✓ | Accordion children's nodes |
| defaultOpen | [string | string[]] | — | ✕ | Default open item(s) * |
| elementType | ElementType | section | ✕ | Type of element used as wrapper |
| stayOpen | bool | — | ✕ | Item stay open when another one is also opened |
(*) If this attribute is an array, then the stayOpen parameter should also be set.
On top of the API options, the components accept additional attributes. If you need more control over the styling of a component, you can use style props and escape hatches.
AccordionItem Props
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
| children | ReactNode | — | ✓ | Item children node |
| elementType | ElementType | article | ✕ | Type of element used as wrapper |
| id | string | — | ✓ | Item id |
On top of the API options, the components accept additional attributes. If you need more control over the styling of a component, you can use style props and escape hatches.
AccordionHeader Props
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
| children | ReactNode | — | ✓ | Header children node |
| elementType | ElementType | h3 | ✕ | Type of element used as wrapper |
| slot | ReactNode | — | ✕ | Side slot in the header |
On top of the API options, the components accept additional attributes. If you need more control over the styling of a component, you can use style props and escape hatches.
AccordionContent Props
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
| children | ReactNode | — | ✓ | Content children node |
On top of the API options, the components accept additional attributes. If you need more control over the styling of a component, you can use style props and escape hatches.
Icons
This component uses the Icon component internally. To ensure correct rendering, please refer to the Icon component documentation for setup instructions.