---
title: Accordion
description: Stacked disclosure component with single or multiple expanded sections.
sidebar:
  order: 7
search:
  tags: [disclosure, expand, collapse, sections]
---

# Accordion

> Stacked disclosure component with single or multiple expanded sections.

## Import

```tsx
import { Accordion } from 'moraine'
```

## Slot Structure

Stacked item list where each item has a clickable header and collapsible content.

```text
root
└── item (×n)
    ├── header
    │   └── trigger
    │       ├── leading (Icon, optional)
    │       ├── label (optional)
    │       └── trailing (Icon, optional)
    └── content (optional)
```

## Examples

### Single

Single-open mode with controlled state and icon leading/trailing slots.

```tsx
function Single() {
  const [openValue, setOpenValue] = createSignal<string[]>(['shipping'])

  return (
    <div class="w-lg space-y-3">
      <Accordion
        value={openValue()}
        onChange={setOpenValue}
        items={[
          {
            value: 'shipping',
            label: 'Shipping information',
            leading: 'i-lucide-truck',
            content:
              'Orders are processed in 1-2 business days and delivered in 3-5 business days.',
          },
          {
            value: 'returns',
            label: 'Returns policy',
            leading: 'i-lucide-rotate-ccw',
            content: 'Returns are accepted within 30 days of delivery.',
          },
          {
            value: 'support',
            label: 'Support',
            leading: 'i-lucide-life-buoy',
            content: 'Reach support any time at support@example.com.',
          },
        ]}
      />

      <p class="text-xs text-muted-foreground">Current open value: {openValue()?.[0] ?? 'none'}</p>
    </div>
  )
}
```

### Multiple

Multiple-open mode with custom trailing icons.

```tsx
function Multiple() {
  return (
    <Accordion
      multiple
      defaultValue={['a']}
      items={[
        {
          value: 'a',
          label: 'Account setup',
          content: 'Create your account and verify email.',
        },
        {
          value: 'b',
          label: 'Team invite',
          content: 'Invite teammates to your workspace.',
        },
        {
          value: 'c',
          label: 'Billing',
          content: 'Add a payment method to continue.',
        },
      ]}
      classes={{
        root: 'max-w-xl rounded-lg b-1 b-border border-border bg-background',
        trigger: 'px-4',
        content: 'px-4 text-foreground',
      }}
    />
  )
}
```

### Disabled + Custom Content

Mix disabled items with rich JSX content blocks.

```tsx
function DisabledCustomContent() {
  return (
    <Accordion
      defaultValue={['setup']}
      trailing="icon-plus"
      items={[
        {
          value: 'setup',
          label: 'Setup checklist',
          leading: 'i-lucide-list-checks',
          content: (
            <div class="space-y-2">
              <p>Complete these steps before inviting your team:</p>
              <ul class="pl-5 list-disc space-y-1">
                <li>Create workspace profile</li>
                <li>Configure authentication</li>
                <li>Enable notifications</li>
              </ul>
              <div class="text-xs text-muted-foreground p-2 rounded-md bg-muted">
                Tip: You can finish the checklist later from Settings.
              </div>
            </div>
          ),
        },
        {
          value: 'security',
          label: 'Security review (Locked)',
          leading: 'i-lucide-shield-check',
          disabled: true,
          content: 'Available on Pro plan and above.',
        },
        {
          value: 'integrations',
          label: 'Integrations',
          leading: 'i-lucide-plug',
          content: (
            <div class="pt-2 space-y-2">
              <p>Connect your tools to automate the workflow.</p>
              <p>
                Supported: <strong>GitHub</strong>, <strong>Slack</strong>, and{' '}
                <strong>Notion</strong>.
              </p>
            </div>
          ),
        },
      ]}
      classes={{
        root: 'max-w-xl rounded-lg b-1 b-border border-border bg-background',
        trigger: 'px-3',
        content: 'px-4 text-foreground',
      }}
    />
  )
}
```

## API Reference

### Attributes

#### `root`

Container that owns the accordion item collection and shared state attributes.

##### Data Attributes

| Attribute | Type | Description |
| --- | --- | --- |
| data-disabled | string \| undefined | Present when the component or item is disabled. |

#### `item`

Wrapper for one accordion entry, including its header trigger and collapsible panel.

#### `header`

Heading row that contains the interactive trigger for an item.

##### ARIA Attributes

| Attribute | Type | Description |
| --- | --- | --- |
| role | string | Defines the semantic role exposed to assistive technology. |

#### `trigger`

Button users activate to expand or collapse an item.

##### ARIA Attributes

| Attribute | Type | Description |
| --- | --- | --- |
| aria-controls | boolean \| string \| undefined | References the controlled element while the related content is mounted. |
| aria-expanded | boolean \| string \| undefined | Indicates whether the controlled content is expanded. |

#### `leading`

Optional icon or visual placed before the item label.

#### `label`

Text label displayed inside the item trigger.

#### `trailing`

Optional icon placed after the label, commonly used for the disclosure indicator.

#### `content`

Panel that contains the item content when expanded.

##### CSS Variables

| Attribute | Type | Description |
| --- | --- | --- |
| --mo-collapsible-content-height | string | CSS custom property exposed by this slot. |

##### ARIA Attributes

| Attribute | Type | Description |
| --- | --- | --- |
| aria-labelledby | boolean \| string \| undefined | References the element that labels the control or region. |
| role | string | Defines the semantic role exposed to assistive technology. |

### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| class | ClassValue | — | Class applied to the component root or trigger element. |
| classes | AccordionT.Classes \| undefined | — | — |
| collapsible | boolean \| undefined | true | Whether the last expanded item can be collapsed. |
| defaultValue | string[] \| undefined | [] | Default list of expanded item values for uncontrolled usage. |
| disabled | boolean \| undefined | false | Whether the entire accordion is disabled. |
| id | string \| undefined | — | Unique identifier for the accordion root element. |
| items | AccordionT.Item[] \| undefined | — | Array of accordion items to render. |
| loopFocus | boolean \| undefined | true | Whether arrow-key focus wraps from the last trigger to the first and vice versa. |
| multiple | boolean \| undefined | false | Whether multiple accordion items can be expanded at the same time. |
| onChange | ((value: string[]) => void) \| undefined | — | Callback when the expanded item values change. |
| ref | JSX.HTMLElementTags["div"] extends { ref?: infer Ref; } ? Ref : never \| undefined | — | — |
| style | JSX.CSSProperties \| undefined | — | — |
| styles | AccordionT.Styles \| undefined | — | — |
| trailing | IconT.Name | icon-chevron-down | Trailing icon name for all accordion items. |
| unmountOnHide | boolean \| undefined | true | Whether to unmount accordion content when hidden. |
| value | string[] \| undefined | — | Controlled list of expanded item values. |

### Items

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| content | JSX.Element | — | Content to display when the accordion item is expanded. |
| disabled | boolean \| undefined | false | Whether the accordion item is disabled. |
| label | JSX.Element | — | Header label for the accordion item. |
| leading | IconT.Name | — | Leading icon name for the accordion item. |
| value | string \| undefined | — | Unique value for the accordion item. |

### ARIA

Accessibility attributes and roles emitted by the component markup.

| Attribute | Type | Description |
| --- | --- | --- |
| aria-controls | boolean \| string \| undefined | References the controlled element while the related content is mounted. |
| aria-expanded | boolean \| string \| undefined | Indicates whether the controlled content is expanded. |
| aria-hidden | boolean \| string \| undefined | Hides decorative content from assistive technology. |
| aria-labelledby | boolean \| string \| undefined | References the element that labels the control or region. |
| role | string | Defines the semantic role exposed to assistive technology. |

### Data Attributes

State and slot attributes exposed for styling hooks and selectors.

| Attribute | Type | Description |
| --- | --- | --- |
| data-disabled | string \| undefined | Present when the component or item is disabled. |
| data-slot | string | Identifies the rendered slot for styling hooks and selectors. |
