Accordion

A vertically stacked set of collapsible panels.

Installation

pnpm add @wandercom/design-system-web

Usage

The Accordion component displays a vertically stacked set of panels, only one of which can be expanded at a time (in uncontrolled mode).

Uncontrolled

15 lines
import { Accordion } from '@wandercom/design-system-web/ui/accordion';

export function Example() {
  return (
    <Accordion
      items={[
        {
          id: '1',
          title: 'What is the capital of Italy?',
          content: 'The capital of Italy is Rome.',
        },
      ]}
    />
  );
}
Loading example...

Controlled

Pass value and onValueChange to control which items are open. Add multiple to allow more than one panel open at a time.

15 lines
import { Accordion } from '@wandercom/design-system-web/ui/accordion';
import { useState } from 'react';

export function Example() {
  const [value, setValue] = useState<(string | null)[]>(['1']);

  return (
    <Accordion
      items={items}
      multiple
      onValueChange={setValue}
      value={value}
    />
  );
}
Loading example...

Compound components

For custom rendering, compose the primitives directly. Use AccordionRoot so the parts share the same Base UI context exported by this package.

21 lines
import {
  AccordionItem,
  AccordionPanel,
  AccordionRoot,
  AccordionTrigger,
} from '@wandercom/design-system-web/ui/accordion';

export function Example() {
  return (
    <AccordionRoot defaultValue={['pricing']} multiple>
      <AccordionItem value="pricing">
        <AccordionTrigger>Pricing</AccordionTrigger>
        <AccordionPanel>Set nightly rates, discounts, and seasonal pricing.</AccordionPanel>
      </AccordionItem>
      <AccordionItem value="availability">
        <AccordionTrigger>Availability</AccordionTrigger>
        <AccordionPanel>Block dates and configure minimum stays.</AccordionPanel>
      </AccordionItem>
    </AccordionRoot>
  );
}
Loading example...

Props

Accordion

items

Array<{ id: string; title: ReactNode; content: ReactNode }>
Array of accordion items. `id` becomes the value key.

slots?

{ root?: string; item?: string; trigger?: string; panel?: string }
Per-slot className overrides.

value?

(string | null)[]
Controlled list of expanded item ids.

defaultValue?

(string | null)[]
Initial list of expanded item ids for uncontrolled usage.

onValueChange?

(value: (string | null)[], details: AccordionRootChangeEventDetails) => void
Callback fired when the expanded items change.

multiple?

boolean
Allow multiple items to be expanded at the same time. Defaults to false.

disabled?

boolean
Disables every trigger when true.

loopFocus?

boolean
Loop keyboard focus back to the first item from the last. Defaults to true.

keepMounted?

boolean
Keep panel content mounted in the DOM while collapsed. Defaults to false.

hiddenUntilFound?

boolean
Use the browser's `hidden='until-found'` so in-page search can expand a collapsed panel. Overrides `keepMounted`.

orientation?

'horizontal' | 'vertical'
Orientation for keyboard navigation. Defaults to "vertical".

AccordionRoot

Accepts every prop documented above for Accordion (value, defaultValue, onValueChange, multiple, disabled, loopFocus, keepMounted, hiddenUntilFound, orientation), plus standard HTML <div> attributes including className. See the Base UI Accordion docs for the full surface.

AccordionItem

value

string
Unique value identifying this item. Used as the key in the accordion `value`.

className?

string
Additional CSS classes for the item container.

AccordionTrigger

Renders inside an <h3> provided by Base UI's Accordion.Header. Accepts standard <button> props plus className. The chevron is appended automatically and rotates when the panel is open via the data-[panel-open] attribute.

AccordionPanel

Accepts standard <div> props plus className. Animates height using the --accordion-panel-height CSS variable provided by Base UI.

Accessibility

Built on Base UI's Accordion primitive, which follows the WAI-ARIA Accordion pattern.

Keyboard interaction:

  • ArrowDown / ArrowUp - Move focus between triggers (vertical orientation).
  • ArrowLeft / ArrowRight - Move focus between triggers (horizontal orientation).
  • Home / End - Move focus to the first / last trigger.
  • Enter / Space - Toggle the focused panel.

Each trigger is linked to its panel via aria-controls. Open state is exposed through aria-expanded on the trigger. The chevron rotation respects animation by toggling on data-[panel-open], which CSS will skip when prefers-reduced-motion: reduce is set on the surrounding utilities.

Accordion