卡片
概述
将相关内容与操作分组到带边框、带阴影表面中的容器组件。SCard 是 UI-only 组件:卡片唯一的真实逻辑是内容折叠,由已准入的 Aria collapsible 家族(CollapsibleRoot / CollapsibleContent / CollapsibleTrigger)提供,其余 header/title/description/footer 装饰节点由 UI 层自持,样式来自 cardVariants 配方(8 个槽、6 种尺寸、scrollable/split 开关)。
适用于仪表盘、资料卡片、设置面板,或任何需要「带标题、分区块」的容器内容。重复性的数据行请优先使用 list 或 table;浮动或模态表面请优先使用 popover/dialog。
卡片默认可折叠——内容区带动画展开/收起,可用 v-model:open 驱动。Aria 层不存在 card 家族:装饰节点仅为呈现服务,因此留在 UI 层,并保留 data-vean-card-* 属性供样式与测试使用。
用法
特性
- 🧩 UI-only 复用已准入原语 — 折叠行为来自 Aria
collapsible家族;SCard自持 chrome,并通过provideCollapsibleUi注入cardVariants类 - 🧱 复合结构 —
header/title/description/content/footer以及title-leading/title-trailing/extra插槽 - 🔽 可折叠 — 内容区带动画展开/收起(基于
CollapsibleRoot/CollapsibleContent),由v-model:open/defaultOpen控制 - ➗ 分区 —
split在标题/内容/底部之间添加divide-y分隔线 - 📜 可滚动内容 —
scrollable使内容区在超出卡片高度时滚动 - 🎨 6 种尺寸 — 匹配
ThemeSize的 xs–2xlsize - 🎛️ 逐部件控制 —
title/description支持 prop 或插槽;headerProps/contentProps/footerProps/titleProps转发到各部件 - ♿ 无障碍友好 — 标题渲染为真实
<h3>,折叠触发器暴露aria-expanded,axe-core零违规
组件家族
SCard— 入口组件;渲染CollapsibleRoot(defaultOpen: true)加header/title-root/title/description/content/footer节点,并把cardVariants配方交给provideCollapsibleUiSCardCollapsibleTrigger— 绑定卡片状态的折叠触发器;默认渲染 chevronSButtonIcon,并通过插槽 props 暴露open状态
演示
基础
Title
Card description
标题插槽
Title
Trailing仅内容
可折叠
Title
API
Aria 层
9Card
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
class | ClassValue | — | 应用于根元素的额外类名。 |
size | ThemeSize | — | 组件的视觉尺寸。 |
ui | Partial<CardUi> | — | 该组件的按槽类重写。 |
scrollable | boolean | false | 如果为 true,当根元素具有高度且内容高度超过根元素时,内容将支持滚动 |
split | boolean | false | 如果为 true,卡片将在标题、内容和页脚之间添加分隔线 |
title | string | — | 由组件渲染的标题文本。 |
titleAs | AsTag | 'h3' | 标题元素的渲染效果。
由于卡片嵌套深度各异,因此标题级别不能是
固定的:在卡片包含首个标题的界面中,默认的 `h3` 会直接位于页面 `<h1>` 之下,
这会导致标题顺序不正确。
请传入与周围大纲结构相匹配的级别。 |
description | string | — | 由组件渲染的描述文本。 |
headerProps | CardHeaderProps | — | 转发至 header 元素的属性。 |
contentProps | CardContentProps | — | 转发给内容元素的属性。 |
footerProps | CardFooterProps | — | 转发至页脚元素的属性。 |
titleRootProps | CardTitleRootProps | — | 传递给标题根元素的属性。 |
titleProps | CardTitleProps | — | 传递给 title 元素的属性。 |
descriptionProps | CardDescriptionProps | — | 已将属性转发至 description 元素。 |
defaultOpen | boolean | — | 可折叠组件在初始渲染时的展开状态。
当不需要控制其展开状态时使用。 |
open | boolean | — | 可折叠组件的受控展开状态。可通过 `v-model` 进行绑定。 |
disabled | boolean | — | 当值为 `true` 时,禁止用户与可折叠区域进行交互。 |
unmountOnHide | boolean | — | 当值为 `true` 时,该元素将在关闭状态下被卸载。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
事件
| 事件名 | 参数 | 描述 |
|---|---|---|
update:open | boolean | 当可折叠组件的展开状态发生变化时,会调用此事件处理程序。 |
插槽
| 插槽名 | 参数 | 描述 |
|---|---|---|
default | — | 默认插槽的自定义内容。 |
header | — | 用于页眉插槽的自定义内容。 |
title | — | 标题槽位的自定义内容。 |
title-leading | — | 标题首行位置的自定义内容。 |
title-trailing | — | 标题末尾插槽的自定义内容。 |
extra | — | 额外槽位的自定义内容。 |
footer | — | 页脚位置的自定义内容。 |
description | — | 描述字段的自定义内容。 |
CardCollapsibleTrigger
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
disabledCollapsible | boolean | — | 当值为 `true` 时,禁止用户切换折叠功能。 |
type | ButtonType | 'button' | 按钮元素的类型。可以是 'button'、'submit' 或 'reset' 之一。 |
disabled | boolean | — | 该组件是否已禁用。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
CardContent
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
forceMount | boolean | — | 用于在需要更多控制时强制挂载。
在使用 Vue 动画库控制动画时非常有用。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
CardDescription
CardFooter
CardHeader
CardRoot
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
defaultOpen | boolean | — | 可折叠组件在初始渲染时的展开状态。
当不需要控制其展开状态时使用。 |
open | boolean | — | 可折叠组件的受控展开状态。可通过 `v-model` 进行绑定。 |
disabled | boolean | — | 当值为 `true` 时,禁止用户与可折叠区域进行交互。 |
unmountOnHide | boolean | — | 当值为 `true` 时,该元素将在关闭状态下被卸载。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
事件
| 事件名 | 参数 | 描述 |
|---|---|---|
update:open | boolean | 当可折叠组件的展开状态发生变化时,会调用此事件处理程序。 |
CardTitle
CardTitleRoot
注意事项
架构与对标差异
Card 是 Aria 准入规则的 UI-only 判例:它唯一的真实逻辑是折叠,而已准入的 collapsible 家族已经提供,因此 Aria 层不存在 card 家族。SCard 负责结构编排(header/footer 显隐、默认标题/描述),并把配方的 root / content / trigger 槽交给 provideCollapsibleUi,让 CollapsibleRoot / CollapsibleContent / CollapsibleTrigger 从 cardVariants 解析各自的类。这与 shadcn/ui 的「组合优先」一致;而 Ant Design、Element Plus、Mantine、Naive UI 则提供带 title/extra/actions prop 的单一样式化卡片。VeanUI 通过 CollapsibleRoot 使卡片默认可折叠——这是多数对标库未提供的刻意扩展;split 与 scrollable 通过配方变体而非布局 prop 切换。
| 能力 | VeanUI | shadcn/ui | Ant Design Card | Element Plus Card | Mantine Card | Naive UI Card |
|---|---|---|---|---|---|---|
| Aria/样式分离 | ✅ | ✅ | — | — | — | — |
| 标题 / 描述 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| 底部 | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| 操作(extra)插槽 | ✅ | — | ✅ | ✅ | ✅ | ✅ |
| 可折叠内容 | ✅ | — | — | — | — | — |
| 分区 / 分隔线 | ✅ | — | ✅ | ✅ | ✅ | — |
| 可滚动内容 | ✅ | — | ✅ | — | — | ✅ |
| 尺寸变体(6) | ✅ | — | ✅ | ✅ | ✅ | ✅ |
逐部件 *Props 通道 | ✅ | ✅ | — | — | — | — |
— = 不支持或采用不同交互模型。
运行时注意
- 卡片默认可折叠且默认展开(
defaultOpen: true)。收起时内容(及底部)会移出文档流;底部显隐取决于展开状态。 - 标题渲染为固定的
<h3>。若页面标题层级不同,请包裹或重设标题,避免跳级。 - 内容节点带有
tabindex="-1",使可滚动主体能接收程序化聚焦;它本身不是交互控件,可通过contentProps覆盖。 SCardCollapsibleTrigger渲染的是纯图标按钮,请传入aria-label(如aria-label="Toggle card content")为其提供可访问名称。split使用divide-y;可结合size控制纵向节奏。scrollable仅在卡片高度受限时生效。- 内容内边距会根据
data-header-visible/data-footer-visible(及折叠data-state)自动调整,纯内容卡片也能保持视觉平衡。
Roadmap
核心卡片 API 无阻塞缺口。交互式操作栏(CardActions 快捷方式)与 hover 抬升样式变体为已评估增强项,记录在 docs/roadmap/README.md。
FAQ
如何构建带标题与描述的卡片?
传入 title/description prop(或对应插槽):
<SCard title="设置" description="管理你的偏好">
<p>主体内容</p>
</SCard>如何在头部添加操作?
使用 extra 插槽(或在标题两侧使用 title-leading/title-trailing):
<SCard title="个人资料">
<template #extra><SButton variant="pure">编辑</SButton></template>
<p>主体内容</p>
</SCard>如何制作可折叠卡片?
用 v-model 绑定 open(或设置 default-open):
<SCard v-model:open="open" title="可折叠">
<p>此内容可以折叠。</p>
</SCard>如何让内容可滚动?
限制卡片高度并设置 scrollable:
<SCard scrollable class="h-80" title="长列表">
<p v-for="i in 50" :key="i">第 {{ i }} 行</p>
</SCard>如何用分隔线分隔区块?
设置 split,在标题/内容/底部之间添加 divide-y 分隔线:
<SCard split title="分区">
<template #footer><SButton>保存</SButton></template>
<p>主体内容</p>
</SCard>