手风琴
概述
一组垂直(或水平)堆叠的交互式标题,每个标题可展开一块内容,支持单选或多选展开。SAccordion 组合 AccordionRoot/AccordionItem/AccordionHeader/AccordionTrigger/AccordionContent/AccordionDescription 这一系列 Aria 基础组件(零样式)与 accordionVariants 样式配方(8 个槽、6 种尺寸)。
适用于 FAQ、设置分组、可折叠导航,或任何「展开显示」的模式(一次打开一个或多个区块)。当一次只需显示一个面板且每个面板有持久标签时请优先使用 tabs;单个独立折叠区块请优先使用 collapsible。
SAccordion 通过 AccordionCompact 聚合这些基础组件,并以 items 数据驱动,提供 item/leading/title/trigger-icon/content 插槽。需要完全自定义组合时,可直接使用 Aria 层的 AccordionRoot 系列基础组件。
用法
特性
- 🧩 Aria/样式分离 —
AccordionCompact聚合 6 个基础组件并以items驱动;SAccordion只注入样式并转发插槽/事件 - 🎚️ 单选 / 多选 —
multiple切换单开/多开(M extends boolean泛型);collapsible允许全部关闭 - ⌨️ 键盘导航 — 方向键在触发器间移动(
useArrowNavigation),遵循orientation与dir;触发器暴露aria-expanded/aria-disabled/data-state - 🧭 方向 —
vertical(默认)或horizontal - ⛔ 禁用条目 — 逐条
disabled与根disabled传播 - 🎨 6 种尺寸 — 匹配
ThemeSize的 xs–2xlsize - 🔧 完全可定制 — 触发器支持
as/asChild,leading/title/trigger-icon/content/item插槽,以及逐槽ui覆盖 - ♿ 无障碍友好 — 真实
<button>触发器带aria-expanded,经data-state实现折叠动画,axe-core零违规
组件家族
SAccordion(样式层)— 入口包装组件;accordionVariants配方配合动态插槽转发AccordionRoot(Aria)— 状态所有者;useSelection管理单选/多选modelValue,暴露dir/orientationAccordionItem(Aria)— 单个区块;从根派生 open/disabled 状态并接入方向键导航AccordionHeader(Aria)— 承载触发器的头部容器AccordionTrigger(Aria)—<button>触发器;aria-expanded/aria-disabled/data-state与点击切换AccordionContent(Aria)— 带动画的折叠内容(CollapsibleContent)AccordionDescription(Aria)— 内容中的默认描述文本AccordionCompact(Aria)— 聚合组件;将items迭代为条目并暴露自定义插槽
演示
基础
自定义图标
自定义样式
API
Aria 层
8Accordion<T, M>
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
class | ClassValue | — | 根类 |
size | ThemeSize | — | 组件的视觉尺寸。 |
ui | Partial<AccordionUi> | — | 该组件的按槽类重写。 |
items | T[] | — | 由该组件渲染的项。 |
itemProps | AccordionItemProps | — | 转发给 item 元素的属性。 |
headerProps | AccordionHeaderProps | — | 转发至 header 元素的属性。 |
triggerProps | AccordionTriggerProps | — | 转发给触发元素的属性。 |
contentProps | AccordionContentProps | — | 转发给内容元素的属性。 |
descriptionProps | AccordionDescriptionProps | — | 已将属性转发至 description 元素。 |
collapsible | boolean | false | 当 type 为 "single" 时,允许通过点击打开项的触发器来折叠内容。
当 type 为 "multiple" 时,此属性无效。 |
dir | Direction | 'ltr' | 如适用,请指定折叠面板的阅读方向。若未指定,则默认采用 LTR(从左到右)阅读模式。 |
disabled | boolean | false | 当值为 `true` 时,禁止用户与折叠面板及其所有项目进行交互 |
orientation | DataOrientation | 'vertical' | 折叠面板的显示方向。 |
unmountOnHide | boolean | `true` | 当值为 `true` 时,该元素将在关闭状态下被卸载。 |
modelValue | (M extends true ? string[] : string) | — | 所选项的受控值。
当需要控制项的状态时使用此属性。可通过 `v-model` 进行绑定。 |
defaultValue | (M extends true ? string[] : string) | — | 所选项的默认值。
当需要设置项的初始状态时,请使用此选项。 |
multiple | M | — | 用于确定一次可选择“单个”还是“多个”项目。
此属性将覆盖根据 `modelValue` 和 `defaultValue` 推断出的类型。 |
selectionBehavior | SelectionBehavior | 'toggle' | 集合中多选功能应如何工作。 |
事件
| 事件名 | 参数 | 描述 |
|---|---|---|
update:modelValue | M extends true ? string[] : string | — |
插槽
| 插槽名 | 参数 | 描述 |
|---|---|---|
item | AccordionCompactSlotProps<T, M> | 项插槽的自定义内容。 |
leading | AccordionCompactSlotProps<T, M> | 为首行位置提供自定义内容。 |
title | AccordionCompactSlotProps<T, M> | 标题槽位的自定义内容。 |
trigger-icon | AccordionCompactSlotProps<T, M> | 触发图标槽位的自定义内容。 |
content | AccordionCompactSlotProps<T, M> | 内容插槽的自定义内容。 |
AccordionCompact<T, M>
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
items | T[] | — | 由该组件渲染的项。 |
itemProps | AccordionItemProps | — | 转发给 item 元素的属性。 |
headerProps | AccordionHeaderProps | — | 转发至 header 元素的属性。 |
triggerProps | AccordionTriggerProps | — | 转发给触发元素的属性。 |
contentProps | AccordionContentProps | — | 转发给内容元素的属性。 |
descriptionProps | AccordionDescriptionProps | — | 已将属性转发至 description 元素。 |
collapsible | boolean | false | 当 type 为 "single" 时,允许通过点击打开项的触发器来折叠内容。
当 type 为 "multiple" 时,此属性无效。 |
dir | Direction | 'ltr' | 如适用,请指定折叠面板的阅读方向。若未指定,则默认采用 LTR(从左到右)阅读模式。 |
disabled | boolean | false | 当值为 `true` 时,禁止用户与折叠面板及其所有项目进行交互 |
orientation | DataOrientation | 'vertical' | 折叠面板的显示方向。 |
unmountOnHide | boolean | `true` | 当值为 `true` 时,该元素将在关闭状态下被卸载。 |
modelValue | (M extends true ? string[] : string) | — | 所选项的受控值。
当需要控制项的状态时使用此属性。可通过 `v-model` 进行绑定。 |
defaultValue | (M extends true ? string[] : string) | — | 所选项的默认值。
当需要设置项的初始状态时,请使用此选项。 |
multiple | M | — | 用于确定一次可选择“单个”还是“多个”项目。
此属性将覆盖根据 `modelValue` 和 `defaultValue` 推断出的类型。 |
selectionBehavior | SelectionBehavior | 'toggle' | 集合中多选功能应如何工作。 |
事件
| 事件名 | 参数 | 描述 |
|---|---|---|
update:modelValue | M extends true ? string[] : string | — |
插槽
| 插槽名 | 参数 | 描述 |
|---|---|---|
item | AccordionCompactSlotProps<T, M> | 项插槽的自定义内容。 |
leading | AccordionCompactSlotProps<T, M> | 为首行位置提供自定义内容。 |
title | AccordionCompactSlotProps<T, M> | 标题槽位的自定义内容。 |
trigger-icon | AccordionCompactSlotProps<T, M> | 触发图标槽位的自定义内容。 |
content | AccordionCompactSlotProps<T, M> | 内容插槽的自定义内容。 |
AccordionContent
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
forceMount | boolean | — | 用于在需要更多控制时强制挂载。
在使用 Vue 动画库控制动画时非常有用。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
AccordionDescription
AccordionHeader
AccordionItem
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
value | string | — | 折叠面板项的值。折叠面板内的所有项应使用唯一的值。 |
disabled | boolean | — | 当值为 `true` 时,禁止用户与可折叠区域进行交互。 |
unmountOnHide | boolean | — | 当值为 `true` 时,该元素将在关闭状态下被卸载。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
AccordionRoot<M>
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
collapsible | boolean | false | 当 type 为 "single" 时,允许通过点击打开项的触发器来折叠内容。
当 type 为 "multiple" 时,此属性无效。 |
dir | Direction | 'ltr' | 如适用,请指定折叠面板的阅读方向。若未指定,则默认采用 LTR(从左到右)阅读模式。 |
disabled | boolean | false | 当值为 `true` 时,禁止用户与折叠面板及其所有项目进行交互 |
orientation | DataOrientation | 'vertical' | 折叠面板的显示方向。 |
unmountOnHide | boolean | `true` | 当值为 `true` 时,该元素将在关闭状态下被卸载。 |
modelValue | (M extends true ? string[] : string) | — | 所选项的受控值。
当需要控制项的状态时使用此属性。可通过 `v-model` 进行绑定。 |
defaultValue | (M extends true ? string[] : string) | — | 所选项的默认值。
当需要设置项的初始状态时,请使用此选项。 |
multiple | M | — | 用于确定一次可选择“单个”还是“多个”项目。
此属性将覆盖根据 `modelValue` 和 `defaultValue` 推断出的类型。 |
selectionBehavior | SelectionBehavior | 'toggle' | 集合中多选功能应如何工作。 |
事件
| 事件名 | 参数 | 描述 |
|---|---|---|
update:modelValue | M extends true ? string[] : string | — |
AccordionTrigger
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
disabledCollapsible | boolean | — | 当值为 `true` 时,禁止用户切换折叠功能。 |
type | ButtonType | 'button' | 按钮元素的类型。可以是 'button'、'submit' 或 'reset' 之一。 |
disabled | boolean | — | 该组件是否已禁用。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
注意事项
架构与对标差异
AccordionCompact 负责 items 迭代与默认内容装配,所有基础组件保持零样式,仅由 UI 包装组件注入 accordionVariants 类。这与 shadcn/ui 的 headless 分离及 Radix 的 Accordion 原语一致;而 Ant Design、Element Plus、Mantine、Naive UI 则提供 defaultActiveKey/activeKey 配置驱动的折叠。VeanUI 的根使用 useSelection,使单选/多选共享同一状态模型,方向键导航复用菜单家族一致的 useArrowNavigation 组合式函数。
| 能力 | VeanUI | shadcn/ui | Ant Design Collapse | Element Plus Collapse | Mantine Accordion | Naive UI Collapse |
|---|---|---|---|---|---|---|
| Aria/样式分离 | ✅ | ✅ | — | — | — | — |
| 单选 / 多选 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| 可全部关闭 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| 方向键导航 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| 自定义触发器图标 | ✅ | — | ✅ | ✅ | ✅ | ✅ |
数据驱动 items(Compact) | ✅ | — | ✅ | ✅ | ✅ | ✅ |
| 禁用条目 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| 方向(v/h) | ✅ | — | ✅ | ✅ | — | — |
as/asChild 触发器 | ✅ | ✅ | — | — | — | — |
— = 不支持或采用不同交互模型。
运行时注意
- 单选模式(
multiple: false)下仅保持一个区块展开;collapsible(默认true)时点击已展开的触发器会关闭它,使所有区块都关闭。 - 方向键导航要求某个触发器已聚焦;按键遵循
orientation(纵向ArrowUp/ArrowDown,横向ArrowLeft/ArrowRight),并在 RTL 下经dir交换。 unmountOnHide(默认true)卸载已关闭的内容;设为false可使其保留在 DOM 中(例如保留内部表单状态)。- 默认触发器 chevron 依据
data-state由 CSS 旋转;需要不同指示符时通过trigger-icon插槽替换。 - 禁用条目忽略点击、被方向键导航跳过,但仍在 DOM 顺序中可被辅助技术触达。
Roadmap
核心手风琴 API 无阻塞缺口。边框 variant 变体与 left 图标触发器布局为已评估增强项,记录在 docs/roadmap/README.md。
FAQ
如何只允许一次展开一个区块?
单选为默认——将 modelValue 绑定为单个值:
<SAccordion v-model="open" :items="items" />如何允许多个区块同时展开?
设置 multiple 并绑定数组:
<SAccordion v-model="openList" multiple :items="items" />如何允许所有区块关闭?
collapsible 默认 true,点击已展开的触发器即关闭:
<SAccordion collapsible :items="items" />如何自定义触发器图标?
使用 trigger-icon 插槽(左侧图标用 leading):
<SAccordion :items="items">
<template #trigger-icon="{ open }">
<SIcon :icon="open ? 'lucide:minus' : 'lucide:plus'" />
</template>
</SAccordion>如何构建完全自定义的条目?
使用 item 插槽自行组合 Aria 部件:
<SAccordion :items="items">
<template #item="{ item, open }">
<div class="border-b">
<button type="button" :aria-expanded="open">{{ item.title }}</button>
<div v-if="open">{{ item.description }}</div>
</div>
</template>
</SAccordion>