# Accordion

Disclosure rows that open in place.

- Group: Navigation
- Import: `import { Accordion } from '@misoto22/folio'`
- Page: https://ui.misoto22.com/components/accordion/
- Related: tabs

## When to reach for it

The marker is a plus, not a chevron: a plus says “this opens”, a chevron says “there is more below”.

## Anatomy

- **Row** (required) — AccordionItem — one hairline-ruled record, keyed by the value Radix opens and closes it by. The set is the root above it: type="single" with collapsible for an FAQ, type="multiple" for a stack of settings.
- **Heading** (required) — Radix’s Accordion.Header, which is an <h3> and takes no level prop. Every row therefore adds an h3 to the document outline, wherever the accordion happens to sit.
- **Trigger** (required) — The full-width button inside that heading: title against the start edge, marker against the end, py-4. It is also what names the open panel.
- **Marker** (required) — A 16px plus, aria-hidden, rotating 45° into a minus when the row opens. The state it draws is carried for everyone else by aria-expanded on the trigger.
- **Panel** — Radix’s Content — a role="region" labelled by its trigger, mounted only while open, overflow-hidden so its measured height can animate, with pb-4 pe-8 inside so the text stops short of the marker’s column. It carries data-folio-animated, so the open and close are dropped outright for a reader who asked for less motion.

## Best practices

### Do

- Pass collapsible alongside type="single": without it there is no empty value to return to, so the first row the reader opens is a row they can never close again.
- Key each item by something stable rather than by its position — Radix tracks the open row by value, so re-ordering or filtering the list leaves whatever now sits in that slot standing open.
- Write the title as the whole question: it is the accessible name of the panel as well as of the trigger, so a row titled “More” opens a region called “More”.
- Reach for type="multiple" when two rows have to be read against each other — single closes the one the reader was holding in order to open the one they wanted to compare it with.

### Don’t

- A closed row’s content is not in the DOM, so an FAQ built out of these is invisible to find-in-page and prints as a list of questions — anything that has to be searchable or printable belongs in the page.
- The trigger is fixed at h3 by Radix’s header, so an accordion under an <h3> lists its rows as that heading’s siblings and the outline goes flat exactly where it should have nested.
- The panel is overflow-hidden — that is what lets the open height animate — so anything inside that must escape the row’s box has to portal out of it; a menu that renders in place is cut off at the row’s edge.

## Keyboard

- Tab — Moves between rows.
- Enter / Space — Opens or closes the focused row.

## Accordion

Re-export of `AccordionPrimitive.Root`.

Radix root, re-exported. Pass `type="single" collapsible` for an FAQ and `type="multiple"` for a settings stack; Radix's own discriminated union then types `value` correctly for each.

## AccordionItem

One disclosure row: a hairline-ruled trigger and its panel. The marker is a plus that rotates into a minus, not a chevron. A chevron says "there is more below"; a plus says "this opens" — and in a stack of rows the difference decides whether the reader expects navigation or expansion. The panel animates on Radix's own `--radix-accordion-content-height`, so it opens to its real height without measuring anything at the call site.

### Props

- `title` (required) — `ReactNode`. The row's summary — what the reader clicks. Named `title` rather than inherited from the DOM attribute of the same name, which is a tooltip.
- `children` (required) — `ReactNode`.

Also accepts: `Omit<ComponentProps<typeof AccordionPrimitive.Item>, 'children' | 'title'>`.

## Example — default

```tsx
import { Accordion, AccordionItem } from '@misoto22/folio'

<Accordion type="single" collapsible className="w-full">
  <AccordionItem value="install" title="How do I install it?">
    <code className="font-mono text-xs">pnpm add @misoto22/folio</code>, then import the stylesheet once
    at your app root.
  </AccordionItem>
  <AccordionItem value="tailwind" title="Do I need Tailwind?">
    No. The compiled stylesheet is self-contained. If you already compile Tailwind, import the token
    layers instead and skip the second copy of the utilities.
  </AccordionItem>
  <AccordionItem value="router" title="Which router does it assume?">
    None. Components that navigate take <code className="font-mono text-xs">asChild</code>, so you hand
    them your own Link.
  </AccordionItem>
</Accordion>
```

## Example — a settings stack

```tsx
import { Accordion, AccordionItem, Field, Input, Switch } from '@misoto22/folio'

<Accordion type="multiple" defaultValue={['retries']} className="w-full max-w-md">
  <AccordionItem value="retries" title="Retries">
    <Field label="Attempts" hint="How many times a failed job is tried again.">
      <Input type="number" defaultValue={3} />
    </Field>
  </AccordionItem>
  <AccordionItem value="notifications" title="Notifications">
    <Field
      layout="row"
      label="Email me when a job fails"
      description="One message per failure, not per attempt."
    >
      <Switch defaultChecked />
    </Field>
  </AccordionItem>
</Accordion>
```

## Example — the whole question

```tsx
import { Accordion, AccordionItem, Text } from '@misoto22/folio'

<div className="grid w-full gap-8 sm:grid-cols-2">
  <div className="flex flex-col gap-3">
    <Text size="xs" tone="muted">
      Opens a region called More
    </Text>
    <Accordion type="single" collapsible>
      <AccordionItem value="a" title="More">
        Invoices are issued on the first working day of the month.
      </AccordionItem>
      <AccordionItem value="b" title="Details">
        Payment is due 30 days after the issue date.
      </AccordionItem>
    </Accordion>
  </div>
  <div className="flex flex-col gap-3">
    <Text size="xs" tone="muted">
      Opens a region called by its question
    </Text>
    <Accordion type="single" collapsible>
      <AccordionItem value="a" title="When are invoices issued?">
        Invoices are issued on the first working day of the month.
      </AccordionItem>
      <AccordionItem value="b" title="When is payment due?">
        Payment is due 30 days after the issue date.
      </AccordionItem>
    </Accordion>
  </div>
</div>
```
