Accordion
Stacks sections that fold open and shut. Use it to keep long content collapsed to its headings so only what is needed is expanded.
import { Accordion, AccordionItem } from 'neba';
<Accordion defaultValue={['billing']}>
<AccordionItem value="billing" title="How does billing work?" subtitle="Plans and invoices">
You are charged on the first of each month.
</AccordionItem>
<AccordionItem value="regions" title="Where do builds run?">
In the region closest to the default branch.
</AccordionItem>
</Accordion>;Props
Accordion
| Prop | Type | Default | Description |
|---|---|---|---|
| variantshared | 'solid' | 'outline' | 'text' | 'outline' | Weight of the surface: filled, hairline, or none |
| sizeshared | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | The sheet's radius and each section's padding: the same thing size means on Box |
| colorshared | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'primary' | Semantic colour role. Arbitrary colour values are not accepted |
| densityshared | 'default' | 'compact' | 'default' | Padding only: never the height, never the type scale |
| elevationshared | 0 | 1 | 2 | 3 | 0 | Drop shadow depth. 0 means no shadow at all |
| multiple | boolean | false | Whether more than one section may be open. Off by default: closing the last as you open the next is the whole difference between an accordion and a stack of collapsibles |
| value | (string | number)[] | — | Which sections are open, for a controlled accordion |
| defaultValue | (string | number)[] | — | Which start open |
| onValueChange | (value: (string | number)[]) => void | — | Called when the open set changes |
| dividers | boolean | true | Separates the sections with a hairline rather than space. The opposite default from List: an accordion of tiles is a stack of cards, not one thing |
| headingLevel | 1 | 2 | 3 | 4 | 5 | 6 | 3 | Which heading every section's header is in the page outline. 3 under an h2, 4 under an h3 |
| disabled | boolean | false | Unavailable. Every section stops answering |
| hiddenUntilFound | boolean | false | Keeps closed panels in the DOM so the browser's own page search can find and open them. Overrides keepMounted |
| keepMounted | boolean | false | Keeps closed panels in the DOM. For content that is expensive to build, or form state that should survive being folded away |
| children | ReactNode | — | The AccordionItems |
value with onValueChange makes it controlled; defaultValue makes it uncontrolled. The value is an array of the open items' values.
AccordionItem
| Prop | Type | Default | Description |
|---|---|---|---|
| value | string | number | — | Identifies the section to value / defaultValue. Base UI generates one when it is left out |
| title | ReactNode | — | The heading on the fold |
| subtitle | ReactNode | — | A second line under the title |
| startIcon | ReactNode | — | Content before the title |
| action | ReactNode | — | A control pinned to the end of the header, outside the folding button: a button inside a button is markup the browser rewrites |
| lines | number | — | Cuts the title and the subtitle off after this many lines. Unset, both wrap |
| disabled | boolean | false | This section stops folding; the rest keep working |
| children | ReactNode | — | The body |
Examples
variant
The sheet is never filled with colour. Use text inside a Card: the card is already a sheet, so the borders do not double up.
multiple · dividers · action
multiple is off by default, so opening one section closes the one that was open. Turn it on when the sections are a checklist rather than mutually exclusive answers.
dividers rules between the sections, binding them into one block. action is a control slot outside the folding button, so a header can carry a switch and still fold when pressed.
size
headingLevel · lines
Every section's header is a real heading, so it belongs in the page outline at the right depth: headingLevel sets it for the whole stack — 3 under an <h2>, 4 under an <h3>. It lives on Accordion rather than on a section because the sections are siblings, and a run of headings at different levels is an outline that lies.
Pass plain text as title. A heading element passed in would land inside the one the header already is.
lines on a section cuts the title and the subtitle off after that many lines. Unset, both wrap: an FAQ's title is a sentence, and an ellipsis there loses the question.
<Accordion headingLevel={2}>
<AccordionItem lines={2} title="What happens to my data when I close my account?">
…
</AccordionItem>
</Accordion>hiddenUntilFound and keepMounted
hiddenUntilFound keeps closed panels in the DOM so the browser's find-on-page can locate and open them: worth turning on for an FAQ. keepMounted keeps a closed panel's React tree alive.
Accessibility
- The header button and its panel are wired together with
aria-controlsandaria-expanded. - Each header is a real heading element, at
headingLevel(3by default), so the sections are in the document outline. Pass plain text astitle: a heading passed in would be nested inside that one. - The panel opens by animating its
height; the content does not shift inside the panel.