Accordion
An accessible, WAI-ARIA–compliant accordion component for showing and hiding sections of related content. Supports single or multiple open panels with smooth animations and reduced-motion fallback.
Usage
Basic usage
import { Accordion } from '@nofinite/nui';
<Accordion
items={[
{
id: 'faq1',
title: 'Do you offer refunds?',
content: 'Yes, refunds are available within 30 days.',
},
{
id: 'faq2',
title: 'Do you provide customer support?',
content: 'Absolutely! Support is 24/7.',
},
]}
/>;
With default open item
<Accordion
defaultOpenId="faq1"
items={[
{
id: 'faq1',
title: 'Do you offer refunds?',
content: 'Yes, refunds are available within 30 days.',
},
{
id: 'faq2',
title: 'Do you provide customer support?',
content: 'Absolutely! Support is 24/7.',
},
]}
/>
Variants
This component does not expose visual variants via props. Styling is controlled via CSS classes.
Available classes:
.ui-accordion.ui-accordion-item.ui-accordion-header.ui-accordion-panel.ui-accordion-panel--open
Guidelines:
- Use design tokens (CSS variables) for consistent theming.
- Avoid inline styling except for layout-specific overrides.
Sizes
The Accordion does not provide explicit size props.
- Size and spacing are controlled via CSS (
font-size, padding, margins). - To create size variants, override styles via
className.
States
The Accordion supports the following states internally:
<Accordion multiple />
States explained
-
Collapsed (default) Panel content is hidden and not rendered.
-
Expanded Panel content is rendered and visible.
-
Single open mode (default) Opening one item closes all others.
-
Multiple open mode Multiple panels can be expanded simultaneously when
multiple={true}.
Native Props / Composition
The Accordion is a composite component built from semantic HTML:
- Headers render as
<button> - Panels render as
<div role="region">
You can pass a custom class name to the root:
<Accordion
className="my-custom-accordion"
items={
[
/* ... */
]
}
/>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | AccordionItem[] | — | List of accordion sections |
defaultOpenId | string | — | ID of the item that should be open initially |
multiple | boolean | false | Allows multiple panels to be open at the same time |
className | string | "" | Additional class names for the accordion root |
AccordionItem
interface AccordionItem {
id: string;
title: string;
content: React.ReactNode;
}
Behavior Notes
-
The component is uncontrolled after initialization.
-
defaultOpenIdis only used on first render. -
Panel content is only rendered when open, improving performance.
-
Keyboard interaction:
EnterandSpacetoggle the accordion item.
Accessibility
-
Header buttons:
- Render as
<button> - Use
aria-expanded - Linked to panels via
aria-controls
- Render as
-
Panels:
- Use
role="region" - Linked back to headers via
aria-labelledby
- Use
-
Keyboard support:
Tabto focus headersEnter/Spaceto toggle
-
Focus styles are visible via
:focus-visible
Example:
<Accordion
items={[
{
id: 'a11y',
title: 'Accessible by default',
content: 'This accordion follows WAI-ARIA practices.',
},
]}
/>
Layout
- Accordion is block-level and full-width by default.
- Panels expand vertically.
- Smooth height animation with
prefers-reduced-motionfallback.
<Accordion
className="w-full"
items={
[
/* ... */
]
}
/>
Best Practices
Do
- Use clear, concise titles.
- Keep content related within a single accordion.
- Use
multipleonly when users need to compare sections.
Don’t
- Nest accordions excessively.
- Place critical, always-visible content inside collapsed panels.
- Rely on accordion state for essential navigation.