# Accordion

A vertically stacked set of panels that expand and collapse one at a time.

## Installation

```tsx
import {
  Accordion,
  AccordionItem,
  AccordionHeader,
  AccordionTrigger,
  AccordionPanel,
} from "@makeplane/propel/components/accordion";
```
## Usage

```tsx
import {
  Accordion,
  AccordionHeader,
  AccordionItem,
  AccordionPanel,
  AccordionTrigger,
} from "@makeplane/propel/components/accordion";

const ITEMS = [
  {
    value: "what",
    label: "What is Plane?",
    body: "Plane is an open-source project management tool for tracking issues, sprints, and product roadmaps.",
  },
  {
    value: "pricing",
    label: "How does pricing work?",
    body: "Plane is free to self-host. Managed plans add hosting, backups, and support.",
  },
];

export default function BasicDemo() {
  return (
    <Accordion defaultValue={["what"]}>
      {ITEMS.map((item) => (
        <AccordionItem key={item.value} value={item.value}>
          <AccordionHeader>
            <AccordionTrigger label={item.label} />
          </AccordionHeader>
          <AccordionPanel>{item.body}</AccordionPanel>
        </AccordionItem>
      ))}
    </Accordion>
  );
}
```
## Examples

### With icon

Each trigger can carry an `icon` beside its label, matching the header icon in the design.

```tsx
import {
  Accordion,
  AccordionHeader,
  AccordionItem,
  AccordionPanel,
  AccordionTrigger,
} from "@makeplane/propel/components/accordion";
import { Icon } from "@makeplane/propel/components/icon";
import { HelpOutline } from "@makeplane/propel/icons";

const ITEMS = [
  {
    value: "what",
    label: "What is Plane?",
    body: "Plane is an open-source project management tool for tracking issues, sprints, and product roadmaps.",
  },
  {
    value: "pricing",
    label: "How does pricing work?",
    body: "Plane is free to self-host. Managed plans add hosting, backups, and support.",
  },
  {
    value: "import",
    label: "Can I import my existing data?",
    body: "Yes — Plane can import work items from common trackers so you can migrate without losing history.",
  },
];

export default function WithIconDemo() {
  return (
    <Accordion defaultValue={["what"]}>
      {ITEMS.map((item) => (
        <AccordionItem key={item.value} value={item.value}>
          <AccordionHeader>
            <AccordionTrigger
              icon={<Icon icon={HelpOutline} tint="secondary" />}
              label={item.label}
            />
          </AccordionHeader>
          <AccordionPanel>{item.body}</AccordionPanel>
        </AccordionItem>
      ))}
    </Accordion>
  );
}
```

### Multiple

With `multiple`, several panels can stay open at the same time instead of collapsing one another.

```tsx
import {
  Accordion,
  AccordionHeader,
  AccordionItem,
  AccordionPanel,
  AccordionTrigger,
} from "@makeplane/propel/components/accordion";

const ITEMS = [
  {
    value: "what",
    label: "What is Plane?",
    body: "Plane is an open-source project management tool for tracking issues, sprints, and product roadmaps.",
  },
  {
    value: "pricing",
    label: "How does pricing work?",
    body: "Plane is free to self-host. Managed plans add hosting, backups, and support.",
  },
  {
    value: "import",
    label: "Can I import my existing data?",
    body: "Yes — Plane can import work items from common trackers so you can migrate without losing history.",
  },
];

export default function MultipleDemo() {
  return (
    <Accordion multiple defaultValue={["what", "pricing"]}>
      {ITEMS.map((item) => (
        <AccordionItem key={item.value} value={item.value}>
          <AccordionHeader>
            <AccordionTrigger label={item.label} />
          </AccordionHeader>
          <AccordionPanel>{item.body}</AccordionPanel>
        </AccordionItem>
      ))}
    </Accordion>
  );
}
```

### Disabled

A single item can opt out with `disabled`; its trigger dims and ignores pointer and keyboard toggling while siblings stay interactive.

```tsx
import {
  Accordion,
  AccordionHeader,
  AccordionItem,
  AccordionPanel,
  AccordionTrigger,
} from "@makeplane/propel/components/accordion";

const ITEMS = [
  {
    value: "what",
    label: "What is Plane?",
    body: "Plane is an open-source project management tool for tracking issues, sprints, and product roadmaps.",
  },
  {
    value: "pricing",
    label: "How does pricing work?",
    body: "Plane is free to self-host. Managed plans add hosting, backups, and support.",
  },
  {
    value: "import",
    label: "Can I import my existing data?",
    body: "Yes — Plane can import work items from common trackers so you can migrate without losing history.",
  },
];

export default function DisabledDemo() {
  return (
    <Accordion defaultValue={["what"]}>
      {ITEMS.map((item) => (
        <AccordionItem key={item.value} value={item.value} disabled={item.value === "pricing"}>
          <AccordionHeader>
            <AccordionTrigger label={item.label} />
          </AccordionHeader>
          <AccordionPanel>{item.body}</AccordionPanel>
        </AccordionItem>
      ))}
    </Accordion>
  );
}
```

### Controlled

Pass `value` + `onValueChange` to own the open set. External controls mutate that state directly, and trigger clicks flow back through `onValueChange`.

```tsx
import {
  Accordion,
  AccordionHeader,
  AccordionItem,
  AccordionPanel,
  AccordionTrigger,
} from "@makeplane/propel/components/accordion";
import { Button } from "@makeplane/propel/components/button";
import * as React from "react";

const ITEMS = [
  {
    value: "what",
    label: "What is Plane?",
    body: "Plane is an open-source project management tool for tracking issues, sprints, and product roadmaps.",
  },
  {
    value: "pricing",
    label: "How does pricing work?",
    body: "Plane is free to self-host. Managed plans add hosting, backups, and support.",
  },
  {
    value: "import",
    label: "Can I import my existing data?",
    body: "Yes — Plane can import work items from common trackers so you can migrate without losing history.",
  },
];

export default function ControlledDemo() {
  const [value, setValue] = React.useState<string[]>(["what"]);
  return (
    <div className="flex flex-col gap-3">
      <div className="flex flex-wrap items-center gap-2">
        <Button
          variant="secondary"
          size="xs"
          stretch="auto"
          label="Expand all"
          onClick={() => setValue(ITEMS.map((item) => item.value))}
        />
        <Button
          variant="secondary"
          size="xs"
          stretch="auto"
          label="Collapse all"
          onClick={() => setValue([])}
        />
      </div>
      <Accordion multiple value={value} onValueChange={(next) => setValue(next as string[])}>
        {ITEMS.map((item) => (
          <AccordionItem key={item.value} value={item.value}>
            <AccordionHeader>
              <AccordionTrigger label={item.label} />
            </AccordionHeader>
            <AccordionPanel>{item.body}</AccordionPanel>
          </AccordionItem>
        ))}
      </Accordion>
    </div>
  );
}
```
## API Reference

### Accordion

Groups a set of `AccordionItem`s. Single-open by default; pass `multiple` to allow several panels
open at once. Use `defaultValue` (uncontrolled) or `value` + `onValueChange` (controlled) to
drive which items are expanded. Grafts Base UI's accordion behavior onto the styled `Accordion`
frame.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, State<Value>>` | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render. |

### AccordionTrigger

The ready-made accordion trigger: grafts Base UI's trigger behavior onto the styled
`AccordionTrigger`, composing an optional `icon`, the `AccordionTriggerTitle`, and the
`DisclosureIndicator` chevron that rotates when the panel opens.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `label` (required) | `string` | — | Visible trigger label. |
| `render` | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, AccordionTriggerState>` | — | Allows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render. |
| `icon` | `ReactNode` | — | Element rendered before the label (inline-start), e.g. `<Icon icon={Folder} tint="secondary" />`. |
