Skip to main content

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

PropTypeDefaultDescription
itemsAccordionItem[]List of accordion sections
defaultOpenIdstringID of the item that should be open initially
multiplebooleanfalseAllows multiple panels to be open at the same time
classNamestring""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.

  • defaultOpenId is only used on first render.

  • Panel content is only rendered when open, improving performance.

  • Keyboard interaction:

    • Enter and Space toggle the accordion item.

Accessibility

  • Header buttons:

    • Render as <button>
    • Use aria-expanded
    • Linked to panels via aria-controls
  • Panels:

    • Use role="region"
    • Linked back to headers via aria-labelledby
  • Keyboard support:

    • Tab to focus headers
    • Enter / Space to 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-motion fallback.
<Accordion
className="w-full"
items={
[
/* ... */
]
}
/>

Best Practices

Do

  • Use clear, concise titles.
  • Keep content related within a single accordion.
  • Use multiple only 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.