# Accordion

> A stack of headings that each open and close a section below them, one at a time or several at once.

- Name: `accordion`
- Status: beta
- Categories: layout
- File: the install writes `accordion.tsx` to your `ui` alias, `@/components/ui` by default.
- npm packages: `@base-ui/react`, `class-variance-authority`. The install adds them.
- Other registry items: `@arbokui/motion-timing`. The install adds them.
- Gallery: https://arbokui.com/components/accordion
- Registry item: https://arbokui.com/r/accordion.json

## Install

1. Add the registry to `components.json`. Do this first, once:

   ```bash
   npx shadcn@latest registry add "@arbokui=https://arbokui.com/r/{name}.json"
   ```

   The command writes this entry. You can also add it by hand:

   ```json
   {
     "registries": {
       "@arbokui": "https://arbokui.com/r/{name}.json"
     }
   }
   ```

   Without this step, the shadcn CLI looks up `@arbokui` in a public list that Arbokui does not control.

2. Add the theme once per app. It replaces your color tokens with ones checked for contrast:

   ```bash
   npx shadcn@latest add @arbokui/theme
   ```

3. Add the component:

   ```bash
   npx shadcn@latest add @arbokui/accordion
   ```

## Use it for

- Questions and answers, like an FAQ, where people read only the ones they need.
- Long settings or details split into sections, so the page starts short.
- Build each section from AccordionItem, AccordionTrigger, and AccordionPanel. Give each item a value when you control which ones are open.
- Add multiple when people may want to compare two sections side by side.
- Add hiddenUntilFound so the browser's find-in-page can search closed sections and open the match.
- Panels can contain images, links, lists, and controls. Use keepMounted on a panel with local form state so closing it preserves edits.

## Avoid it for

- Content everyone needs to read. Show it on the page.
- Switching between views of the same thing. Use tabs.
- A single section that shows more. Use a details element instead.
- Steps that must happen in order. Use a page per step, or step dots.

## Props

- `Accordion`: `{ value?: string[]; defaultValue?: string[]; onValueChange?: (value: string[], details) => void; multiple?: boolean; disabled?: boolean; hiddenUntilFound?: boolean }`. Root. Holds which sections are open. Takes value, defaultValue, onValueChange, multiple, disabled, and hiddenUntilFound.
- `size`: `"sm" | "md" | "lg"`. Default `"md"`. Accordion. Row height and text size: 36, 44, or 56px rows at the default density.
- `multiple`: `boolean`. Default `false`. Accordion. Lets more than one section be open at once.
- `value`: `string[]`. Accordion. The open items' values, controlled. Use it with onValueChange.
- `defaultValue`: `string[]`. Accordion. The items open at the start when you do not control it.
- `onValueChange`: `(value: string[], details) => void`. Accordion. Called with the open items' values when one opens or closes.
- `hiddenUntilFound`: `boolean`. Default `false`. Accordion or AccordionPanel. Keeps closed panels in the page with hidden='until-found', so find-in-page can open them.
- `disabled`: `boolean`. Default `false`. Accordion or AccordionItem. Blocks opening and closing. It dims.
- `AccordionItem`: `ComponentProps<"div"> & { value?: string }`. Part. One section. A line separates it from the next. value names it for value and defaultValue.
- `AccordionTrigger`: `ComponentProps<"button">`. Part. The heading's button. It renders inside an \<h3> and adds a chevron that turns when the section opens.
- `AccordionPanel`: `ComponentProps<"div">`. Part. The section's content. It grows open and shrinks closed.
- `keepMounted`: `boolean`. Default `false`. AccordionPanel. Keeps the contents mounted while closed, preserving local field values. Hidden controls stay out of the tab order.
- `className`: `string`. On any part. Later classes win. On AccordionPanel it goes on the inner box, where the padding is, so className='pb-6' works.

## Accessibility

- Each trigger is a button inside an \<h3>, so screen reader users can jump between sections by heading.
- The button has aria-expanded and aria-controls. An open panel has role="region", named by its trigger.
- Each trigger is its own tab stop. Enter and Space open and close it. Arrow keys do not move between triggers, as in the current ARIA pattern.
- The chevron is hidden from screen readers. aria-expanded says whether a section is open.
- The heading level is fixed at h3. If the page needs another level, pass render to the header in your copy of accordion.tsx.
- The focus ring uses --ring, which the theme tests hold to 3:1 against the page and cards.
- Opening grows the panel over --motion-duration-base while its contents slide into place and fade in over 1.5 times --motion-duration-slow. Closing fades the contents over --motion-duration-close. Reduced motion removes height, content, and chevron movement; Motion Off removes all transitions.

## Examples

### FAQ

```tsx
<Accordion hiddenUntilFound>
  <AccordionItem value="refunds">
    <AccordionTrigger>Can I get a refund?</AccordionTrigger>
    <AccordionPanel>Yes, within 30 days of your order. We send the money back to the card you paid with.</AccordionPanel>
  </AccordionItem>
  <AccordionItem value="shipping">
    <AccordionTrigger>How long does shipping take?</AccordionTrigger>
    <AccordionPanel>Three to five working days in the US, and up to ten days anywhere else.</AccordionPanel>
  </AccordionItem>
</Accordion>
```

### Editable values that survive closing a section

```tsx
<Accordion multiple defaultValue={["account"]} size="lg">
  <AccordionItem value="account">
    <AccordionTrigger>Account</AccordionTrigger>
    <AccordionPanel keepMounted>
      <label>Display name <input defaultValue="Workspace owner" /></label>
    </AccordionPanel>
  </AccordionItem>
  <AccordionItem value="billing">
    <AccordionTrigger>Billing</AccordionTrigger>
    <AccordionPanel>Your plan, card, and past invoices.</AccordionPanel>
  </AccordionItem>
</Accordion>
```

### Controlled

```tsx
"use client";
import { useState } from "react";

function Sections() {
  const [open, setOpen] = useState<string[]>([]);
  return (
    <Accordion value={open} onValueChange={(value) => setOpen(value as string[])}>
      <AccordionItem value="one">
        <AccordionTrigger>First section</AccordionTrigger>
        <AccordionPanel>Shown when open includes "one".</AccordionPanel>
      </AccordionItem>
    </Accordion>
  );
}
```
