---
title: "折叠面板"
description: "Accordion 用标题按钮展开和收起内容分区：单开或多开、拦截变更、高度动画与页内查找，外观由你决定。"
---

> Documentation Index
> Fetch the complete documentation index at: https://ui-sdk.ndjp.net/llms.txt
> Use this file to discover all available pages before exploring further.

# 折叠面板

`Accordion`（折叠面板）把内容分成若干分区，每个标题是一个按钮，按下展开对应面板。适合 FAQ、设置页、课程大纲这类逐段浏览的内容。只有一块内容要开合时用[可折叠区域](https://ui-sdk.ndjp.net/docs/components/collapsible)；要在同一块空间里切换互斥视图时用[标签页](https://ui-sdk.ndjp.net/docs/components/tabs)。

## 组合

```text
Accordion
├── AccordionItem
│   ├── AccordionHeader
│   │   └── AccordionTrigger
│   └── AccordionPanel
└── AccordionItem
    ├── AccordionHeader
    │   └── AccordionTrigger
    └── AccordionPanel
```

每个 `AccordionItem` 要有唯一的 `value`，触发器放进标题里，面板跟在后面。根的值始终是展开项 value 的数组，即使只允许展开一项。`AccordionHeader` 默认渲染 `h3`，按周围文档结构选用合适的标题级别。

## 用法

```tsx
import {
  Accordion,
  AccordionHeader,
  AccordionItem,
  AccordionPanel,
  AccordionTrigger,
} from "@ui-sdk/react/accordion";

function Example() {
  return (
    <Accordion defaultValue={["shipping"]}>
      <AccordionItem value="shipping">
        <AccordionHeader>
          <AccordionTrigger>配送方式</AccordionTrigger>
        </AccordionHeader>
        <AccordionPanel>标准件 5 到 7 天送达，加急件 2 到 3 天。</AccordionPanel>
      </AccordionItem>
      <AccordionItem value="returns">
        <AccordionHeader>
          <AccordionTrigger>退货政策</AccordionTrigger>
        </AccordionHeader>
        <AccordionPanel>签收后 30 天内可退未使用的商品。</AccordionPanel>
      </AccordionItem>
    </Accordion>
  );
}
```

## 多开与常驻

`multiple` 让多个分区同时展开、互不影响。`collapsible={false}` 则保证总有一项展开：打开的项不能被自己关闭，其触发器报告 `aria-disabled="true"`，常与 `defaultValue` 搭配使用。

```tsx
<Accordion multiple defaultValue={["notifications"]}>
  <AccordionItem value="notifications">
    <AccordionHeader>
      <AccordionTrigger>通知设置</AccordionTrigger>
    </AccordionHeader>
    <AccordionPanel>选择邮件通知的内容与频率。</AccordionPanel>
  </AccordionItem>
  <AccordionItem value="privacy">
    <AccordionHeader>
      <AccordionTrigger>隐私与安全</AccordionTrigger>
    </AccordionHeader>
    <AccordionPanel>开启两步验证，查看已登录设备。</AccordionPanel>
  </AccordionItem>
</Accordion>
```

## 受控与拦截

`value` 加 `onValueChange` 接管展开项。项的 `onOpenChange(open, details)` 先于根的回调运行（包括因别项打开而将要关闭的项），任何一处调用 `details.cancel()` 都会取消整个变更。`details.reason` 说明来源：`trigger-press`、`find-in-page` 或 `imperative`。

```tsx
const [value, setValue] = React.useState(["security"]);

<Accordion value={value} onValueChange={(next) => setValue(next)}>
  <AccordionItem
    value="security"
    onOpenChange={(open, details) => {
      if (!open && !window.confirm("确定关闭安全设置？")) {
        details.cancel();
      }
    }}
  >
    <AccordionHeader>
      <AccordionTrigger>安全</AccordionTrigger>
    </AccordionHeader>
    <AccordionPanel>密码、通行密钥与两步验证。</AccordionPanel>
  </AccordionItem>
</Accordion>;
```

用 `createAccordionController()`（来自 `@ui-sdk/core/accordion`）创建控制器传给 `controller`，可在组件树外调用 `open / close / toggle / setValue / getValue / isOpen / focus`；`multiple` 下还有 `openAll()` 与 `closeAll()`。参见[控制器](https://ui-sdk.ndjp.net/docs/concepts/controllers)。

## 保留内容与页内查找

关闭的面板默认卸载。`keepMounted` 让它隐藏留在页面里，保住输入草稿等局部状态；`hiddenUntilFound` 进一步以 `hidden="until-found"` 保留，浏览器页内查找命中时立即展开（不播放动画），reason 为 `find-in-page`。两者可设在根上，也可设在单个 `AccordionPanel` 上覆盖根的设置。

## 样式与状态

各部件带 `data-slot`：`accordion`、`accordion-item`、`accordion-header`、`accordion-trigger`、`accordion-panel`。项、标题、触发器、面板都带 `data-open` / `data-closed` / `data-disabled`；面板另有 `data-starting-style`（入场首帧）与 `data-ending-style`（退场期间）。

面板把测量到的高度写进 `--accordion-panel-height`（横向开合用 `--accordion-panel-width`），配合状态属性做高度动画：

```css
[data-slot="accordion-panel"] {
  height: var(--accordion-panel-height);
  overflow: hidden;
  transition: height 0.15s;
}
[data-slot="accordion-panel"]:is([data-starting-style], [data-ending-style]) {
  height: 0;
}
[data-slot="accordion-trigger"][data-open] > svg {
  rotate: 180deg;
}
[data-slot="accordion-trigger"][data-disabled] {
  color: gray;
}
```

面板会等退场动画结束再卸载，随后运行项的 `onOpenChangeComplete(open)`；初始就展开的面板直接显示，不播动画。详见[样式与动画](https://ui-sdk.ndjp.net/docs/concepts/styling-and-animation)。

## API

根 `Accordion` 的主要 props：

| Prop | 类型 | 说明 |
| --- | --- | --- |
| `value` | `T[]` | 展开项的 value 数组，传入即受控。 |
| `defaultValue` | `T[]` | 非受控时初始展开的项。 |
| `onValueChange` | `(value, details) => void` | 展开项要变化时运行，`details.cancel()` 可拦截。 |
| `multiple` | `boolean` | 允许多项同时展开，否则开一项关其余。 |
| `collapsible` | `boolean` | 为 `false` 时总保留一项展开。 |
| `disabled` | `boolean` | 禁用全部项，触发器仍可聚焦。 |
| `keepMounted` | `boolean` | 关闭的面板隐藏保留在页面中。 |
| `hiddenUntilFound` | `boolean` | 关闭面板可被页内查找命中并展开。 |
| `controller` | `object` | 用 `createAccordionController()` 创建。 |

`AccordionItem` 另有 `value`、`disabled`、`onOpenChange`、`onOpenChangeComplete`；`AccordionTrigger` 可单独 `disabled`。

## 键盘与无障碍

遵循 WAI-ARIA accordion 模式：

- `Tab` 依次经过每个触发器，禁用的也在内；展开后 `Tab` 进入面板。
- `Enter` / `Space` 开合焦点所在的面板。
- 方向键、`Home`、`End` 在这里不动作：APG 已把它们从该模式移除，屏幕阅读器的标题导航足以到达每个触发器。

每个触发器位于标题元素内，带 `aria-expanded` 并通过 `aria-controls` 指向面板；面板是以触发器命名的 region，屏幕阅读器可把展开的面板列为地标。关闭持有焦点的面板时，焦点回到它的触发器。`multiple` 加「全部展开」时请控制项数，避免地标列表过长。

## 相关组件

- [可折叠区域](https://ui-sdk.ndjp.net/docs/components/collapsible)：只开合一块内容时的轻量选择。
- [标签页](https://ui-sdk.ndjp.net/docs/components/tabs)：在同一空间切换互斥视图。
- [树形视图](https://ui-sdk.ndjp.net/docs/components/tree)：层级更深、需要选择与复选框时。

Source: https://ui-sdk.ndjp.net/docs/components/accordion/index.mdx
