# Accordion

A stack of headings that each open to reveal more.

- Registry name: `accordion` (`@candor/accordion`)
- Tier: Free (MIT). No licence key needed.
- Docs: https://candor.themethodic.com/docs/accordion

## Install

```bash
npx candor-ui add accordion
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/accordion`.
It brings in: motion, tokens, utils.
npm packages: @base-ui/react@^1.9.0, lucide-react@^1.67.0.

## Example

```tsx
"use client"

import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/candor/ui/accordion"

export default function Example() {
  // Everything starts closed: the person opens what they want.
  return (
    <Accordion type="single" collapsible className="w-full max-w-md">
      <AccordionItem value="what">
        <AccordionTrigger>What did the agent change?</AccordionTrigger>
        <AccordionContent>Three records in the billing table. The list is in the run's change log.</AccordionContent>
      </AccordionItem>
      <AccordionItem value="undo">
        <AccordionTrigger>Can it be undone?</AccordionTrigger>
        <AccordionContent>Two of the three can. The third sent an email, which can't be recalled.</AccordionContent>
      </AccordionItem>
      <AccordionItem value="who">
        <AccordionTrigger>Who approved it?</AccordionTrigger>
        <AccordionContent>No one yet. The run is waiting for a decision.</AccordionContent>
      </AccordionItem>
    </Accordion>
  )
}
```

## <Accordion>

Props:

- `className?: string | undefined`

Also takes the props of `Omit<AccordionPrimitive.Root.Props<string>, "className" | "value" | "defaultValue" | "onValueChange" | "multiple">`, `(SingleAccordion | MultipleAccordion)`.

## <AccordionItem>

One heading and its panel. Give it a `value` that is unique in the accordion.

Props:

- `className?: string | undefined`

Also takes the props of `Omit<AccordionPrimitive.Item.Props, "className">`.

## <AccordionTrigger>

The heading that opens and closes its panel, with a chevron that turns over when open. Its children are the heading's text.

Props:

- `className?: string | undefined`

Also takes the props of `Omit<AccordionPrimitive.Trigger.Props, "className">`.

## <AccordionContent>

The panel. It opens and closes to the height Base UI measured, so a change is seen settling rather than snapping; data-motion="none" and the OS's reduced-motion setting make it snap.

Props:

- `className?: string | undefined`: Classes for the panel's content (inside the part that opens and closes).
- `style?: React.CSSProperties | undefined`: Inline styles, merged over the part's motion chain.
- `forceMount?: boolean | undefined`: Keeps the panel in the page, hidden, while it is closed. The same as Base UI's `keepMounted`.

Also takes the props of `Omit<AccordionPrimitive.Panel.Props, "className" | "style">`.

## Motion

- **open** (reveal, size): The panel grows to its full height when the person opens it and shrinks back when they close it. What the app puts inside it doesn't move. Under reduced motion the panel snaps open or shut.

Set with `--cd-motion-accordion-<moment>-*` in your brand file; the families are in the design reference.

## Terms used here

- **Calm level**: What Candor's motion does for a person whose operating system asks for reduced motion: the highest-precedence setting, above the app's own motion level (`none`, `calm`, `subtle` or `expressive`) and the brand file. A theme tunes how a change looks, never when it happens, so nothing depends on seeing the motion. (https://candor.themethodic.com/glossary.md#calm-level)

## Rules for using Candor UI

- Components take plain typed data. Never pass model output as HTML or render it as a link; messages are typed blocks.
- A control that can't be used says why: wrap it in `<Locked reason="…">`, don't just disable it.
- Don't show a value you don't have: leave it out (it renders as —) rather than guess. An estimate is labelled as one.
- Nothing is pre-selected in a decision a person makes, and the agent is named from your app's data, never from model output.
- Your app's words go in `LocaleProvider`: its fixed strings in `strings`, its nouns in `nouns`; entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
