Card 卡片
Card 是带表面样式的布局容器:一层背景、一组圆角,加上标题行、正文和底部操作栏三段固定结构。 它和 Container 共用层级上下文,因此卡片里的卡片会自动深一层。
Card 只负责排版,不负责交互:点击、选中、跳转都由卡片里的内容自己决定。
最小用法
title 是标题,默认插槽是正文。
共享文件夹
任何拿到链接的人都可以查看这个文件夹。
vue
<template>
<Card title="共享文件夹">任何拿到链接的人都可以查看这个文件夹。</Card>
</template>三段结构
卡片的 DOM 永远是三块,缺省的部分不渲染,也不留空隙:
| 区块 | 元素 | 内容 |
|---|---|---|
| 头部 | <header> | title-left 槽位 · 标题列(title / subtitle)· title-right 槽位 |
| 正文 | 无标签的内容容器 | 默认插槽 |
| 底部 | <footer> | footer-left 槽位 + footer-right 槽位 |
标题永远在卡片最顶端:头部没有上方留白,头部的存在只给正文加上边距。头部和底部各自在两组槽位都 为空时才完全不渲染。
变体
variant 决定表面,层级决定圆角与内边距:
浮起
阴影抬升,浮在页面上。
填充
填充底色,视觉重量更低。
表面
只有表面色,没有任何装饰。
描边
一条描边,适合密集排布。
透明
完全透明,只保留圆角与排版。
vue
<template>
<Card variant="elevated" title="浮起">…</Card>
<Card variant="filled" title="填充">…</Card>
<Card variant="surface" title="表面">…</Card>
<Card variant="outlined" title="描边">…</Card>
<Card variant="plain" title="透明">…</Card>
</template>surface 是对话框面板用的表面色,plain 用于把卡片结构套在已有背景上。
标题与副标题
title 和 subtitle 各接受一段纯文本;需要标记、链接、换行这类富内容时改用 title / subtitle 插槽, 插槽优先于属性。副标题由你自己排版,使用插槽时它不会自动获得上边距。
两者的默认节点都是普通 div,不用 h3 / p:组件库不带 reset,中性元素不会被宿主页面的标题、 段落排版(外边距、字号、行高)改写。需要标题层级时用 #title 插槽自己写 <h3>。
公测共享文件夹
邀请链接 7 天后过期。
任何拿到链接的人都可以查看这个文件夹。
vue
<template>
<Card>
<template #title>
<div class="flex items-center gap-2">
<span class="rounded-ds-sm bg-ds-tonal px-2 py-0.5 text-xs text-ds-on-tonal">公测</span>
<span class="text-base leading-6 font-medium">共享文件夹</span>
</div>
</template>
<template #subtitle>
<p class="mt-1 text-sm leading-5 text-ds-muted">邀请链接 <span>7 天后过期</span>。</p>
</template>
任何拿到链接的人都可以查看这个文件夹。
</Card>
</template>标题两侧的槽位
title-left 与 title-right 与标题同一行,分别贴在左右两端,用来放图标、头像、更多操作或关闭按钮。 两个槽位随整个标题栏(标题 + 副标题)的高度垂直居中:只有标题时它们对齐标题行,出现副标题后整栏变高, 槽位内容自动落到两行中间,而不是贴着顶部。
收件箱
左槽位放前置图标。
共享文件夹
邀请链接 7 天后过期
右槽位放操作按钮。
已归档
关闭按钮与标题同一行。
vue
<template>
<Card title="收件箱">
<template #title-left>
<Icon :icon="folderIcon" size="20" />
</template>
左槽位放前置图标。
</Card>
<Card title="共享文件夹" subtitle="邀请链接 7 天后过期">
<template #title-right>
<IconButton :icon="moreIcon" label="更多操作" />
</template>
右槽位放操作按钮。
</Card>
<Card title="已归档">
<template #title-right>
<IconButton :icon="closeIcon" label="关闭" />
</template>
关闭按钮与标题同一行。
</Card>
</template>槽位内容自己决定样式:图标用 Icon,按钮用 IconButton 或 Button。 放多个控件时它们之间会自动留出间距。
底部操作
底部是左右两组槽位:footer-right 放主操作,footer-left 放需要推向行首的次要操作。
只放主操作时:
共享文件夹
邀请链接 7 天后过期
任何拿到链接的人都可以查看这个文件夹。
上次点击:还没有点击
vue
<template>
<Card title="共享文件夹" subtitle="邀请链接 7 天后过期">
<template #title-right>
<IconButton :icon="moreIcon" label="更多操作" />
</template>
任何拿到链接的人都可以查看这个文件夹。
<template #footer-right>
<Button variant="text">取消</Button>
<Button>创建链接</Button>
</template>
</Card>
</template>两侧都要时,把「了解更多」这类入口放进 footer-left:
开启定位服务?
允许其他应用借助此服务确定你的位置。这意味着即使没有应用在运行,也会共享匿名的位置数据。
vue
<template>
<Card title="开启定位服务?">
正文……
<template #footer-left>
<Button variant="text">了解更多</Button>
</template>
<template #footer-right>
<Button variant="text">不同意</Button>
<Button variant="text">同意</Button>
</template>
</Card>
</template>放不下时折成两行
底部是 flex-wrap 容器,右组带 margin-left: auto:两组放得下时同一行左右分开;放不下时右组整组折到 第二行,并且仍然贴右边缘,左组留在第一行左端。
删除后无法恢复
窄卡片里底部会分成两行。
删除后无法恢复
同样两组操作,宽卡片里保持一行。
vue
<Card title="删除后无法恢复" class="w-56">
正文……
<template #footer-left>
<Button variant="text" size="sm">了解更多</Button>
</template>
<template #footer-right>
<Button variant="text" size="sm">稍微想想</Button>
<Button variant="text" size="sm">确认删除</Button>
</template>
</Card>折行判断的是内容实际宽度,不是断点,所以长文案、放大字号、窄容器都会触发同样的两行布局。
内边距
padded 默认为 true,由层级决定内边距。传 false 后卡片只留下背景与圆角,排版完全交给内容—— 整宽色带、图片、列表这类需要贴边的版式用它。
整宽色带贴着卡片边缘
正文自己管内边距。
vue
<template>
<Card :padded="false" variant="outlined">
<div class="bg-ds-tonal px-4 py-3">整宽色带贴着卡片边缘</div>
<div class="px-4 py-3">正文自己管内边距。</div>
<div class="flex justify-end gap-2 px-4 pb-4">
<Button variant="text" size="sm">取消</Button>
<Button size="sm">保存</Button>
</div>
</Card>
</template>带标题的卡片也可以 padded="false",此时头部同样没有内边距。
层级与嵌套
Card 与 Container 共用层级:外层自动是 1,嵌套自动加深,最多三层;圆角与内边距随层级收缩 (rounded-ds-xl / p-4 → rounded-ds-lg / p-3 → rounded-ds / p-2)。
第 1 层
第 2 层
第 3 层
再深也只停在第 3 层。
vue
<template>
<Card title="第 1 层">
<Container>
<Card variant="outlined" title="第 2 层">
<Container>
<Card variant="filled" title="第 3 层">…</Card>
</Container>
</Card>
</Container>
</Card>
</template>需要脱离父层级(例如浮层里的卡片仍要第 1 层的圆角)时,显式传 level:
外层
固定的第 1 层
圆角与内边距按第 1 层计算。
vue
<template>
<Card title="外层">
<Container>
<Card variant="outlined" title="固定的第 1 层" :level="1">…</Card>
</Container>
</Card>
</template>在受限高度里延伸
卡片根是纵向 flex 容器,正文容器带 flex-1 与 min-h-0。因此给卡片加 max-h-* 后,正文可以自己 滚动、头部与底部保持不动——Dialog 的面板就是这么用卡片的。
最近活动
第 1 条活动记录。
第 2 条活动记录。
第 3 条活动记录。
第 4 条活动记录。
第 5 条活动记录。
第 6 条活动记录。
第 7 条活动记录。
第 8 条活动记录。
第 9 条活动记录。
第 10 条活动记录。
第 11 条活动记录。
第 12 条活动记录。
vue
<template>
<Card title="最近活动" class="max-h-52">
<div class="min-h-0 flex-1 overflow-y-auto">
<p v-for="index in 12" :key="index">第 {{ index }} 条活动记录。</p>
</div>
<div class="mt-2 flex shrink-0 justify-end border-t border-ds-line pt-2">
<Button variant="text" size="sm">全部查看</Button>
</div>
</Card>
</template>API
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
variant | "elevated" | "filled" | "surface" | "outlined" | "plain" | "elevated" | 表面样式 |
title | string | — | 标题文本,title 插槽优先 |
subtitle | string | — | 副标题文本,subtitle 插槽优先 |
level | 1 | 2 | 3 | 自动 | 手动指定层级,忽略父容器 |
padded | boolean | true | 是否使用层级对应的内边距 |
插槽
| 名称 | 说明 |
|---|---|
default | 正文 |
title | 自定义标题内容,替换整段标题 |
subtitle | 自定义副标题内容;需要自己加上边距 |
title-left | 标题行左端,与标题垂直居中 |
title-right | 标题行右端,与标题垂直居中 |
footer-left | 左侧操作组,第一行左端 |
footer-right | 右侧操作组,贴右边缘;两组放不下时整组折到第二行 |
结构
- 根元素类名
ds-card:box-border flex flex-col+ 层级圆角 + 变体表面 + 可选内边距, 并provide当前层级给子容器。 - 头部只在有标题文本或标题相关插槽时渲染,类名固定为
flex items-center gap-3; 标题列是min-w-0 flex-1,两端的槽位是flex shrink-0 items-center gap-1,随标题栏高度垂直居中。 - 正文容器是
flex min-h-0 flex-1 flex-col,头部存在时加mt-3。 - 底部只在有
footer-left/footer-right时渲染:mt-3 flex flex-wrap items-center gap-2 pt-3(只有留白,没有分隔线),右组额外带ml-auto。 - 默认标题与副标题是
div(标题truncate text-base leading-6 font-medium,副标题mt-1 text-sm leading-5 text-ds-muted):组件库不带 reset,用中性元素可以避免宿主页面对h1–h6/p的排版(外边距、字号)渗进卡片;需要标题语义时用#title插槽自己写<h3>。 - 颜色、圆角、字体全部来自
--ds-*令牌,class与style透传到根元素。