Card 卡片
用于组织相关内容与操作的中性表面。
用法
Header 放标题与说明,Content 放主体
本月账单
账期 7 月 1 日至 7 月 31 日
组件结构
加粗的是必需部件。
data-scope="card":root · header · title · trigger · description · content · footer
示例
变体
outline 为默认卡面,subtle 淡底嵌入,ghost 用于嵌套
outline
subtle
ghost
横向布局
Card 只提供内容面,方向和媒体尺寸由使用场景决定
七月总结
收入与支出趋势已生成
带媒体
图片或自绘媒体作为普通子节点放入,由内容自己决定比例与圆角
七月总结
媒体与文字共享卡片的统一节奏
整卡可点
interactive 加标题里的 trigger:整张卡片都是点击区,读屏只读到一个链接
季度报告
2026 年第三季度 · 财务部
设计指引
何时使用
- 把一组相关信息收成一个可以整体感知的单元。
- 内容块之间需要视觉边界。
何时不用
- 页面上每一块都使用卡片会使边界失效。
- 只需要一条分隔时,使用分隔线。
特性
- root 必需;header、title、description、content、footer 按内容组合。
outline是默认卡面,subtle用淡底嵌在别的面里,ghost用于嵌套内容不再画面。- 根取 surface 圆角、1px 装饰边、无影,自己不留内衬:放在根下的 header 是一条头部条(一行标题时高 46px、紧凑密度 40px,横向内距 16px,底边一道 1px 内部分隔线),标题取区块标题档、正文色;content 四边内距 16px(紧凑密度纵向 12px)、14px 次级色;footer 与正文同起点、底边留同档内距。直接放进根的媒体贴边铺满;横向布局与媒体比例由使用场景决定。
- 整卡可点用
interactive加标题里的trigger:trigger 是原生链接(给了href)或<button type="button">,路由链接等作者自己的节点用asChild。它的点击区由伪元素铺满整张卡片,卡片里任一处点下去都是在点它;可及名只取 trigger 自己的文字,Tab 位也只有它这一个。不把整张卡片包进<a>:那样读屏会把标题、说明、正文连成一整串链接名逐字念完,卡片里也不能再放按钮(交互元素不能嵌套)。 - 可交互卡片的反馈按根面分:
outline是抬起面,悬停抬高一档海拔(raised → lifted),按下落回 raised 并换到白底阶梯第一档;subtle悬停淡底 200、按下 300;ghost悬停 100、按下 200。抬起只走影、不走位移,减弱动效下仍是淡变。按下只认 trigger 自己的按下,脚部按钮按下时整卡不跟着换面。 - 键盘聚焦 trigger 时焦点环画在整张卡片外沿,与点击区是同一块轮廓;trigger 自己不再画环。
- 脚部叠在点击区之上:里面另放的按钮照常可点、不触发整卡,脚部的空白处也不触发。其余部件都在点击区之下。
data-material="liquid"下,可交互卡片在细指针悬停时描边扫过一道交互光,只播一遍,与 Button 实心钮同一配方;standard 档、粗指针与强制色下不画。时长槽--xh-card-glint-duration。- 不写结构时按标准结构铺(Vue 不写默认插槽、React 不给 children、Web Components 不写部件或只写
root),三端出自同一个结构函数:给了标题或说明才铺页头(标题在前),给了正文、页脚才铺对应一段,内容都归作者。Vue 是title、description、content、footer四个具名插槽,React 是同名的四个属性,Web Components 是宿主直接子节点上的同名slot。整卡可交互的 trigger 要用时手写结构。
组合
最佳实践
- 整卡可点时用
interactive加 trigger,不要自己给根加tabindex与点击处理:那样根没有可及名,读屏只报一个“组”。 - trigger 与根之间的祖先不要再建定位上下文(
position: relative等),否则点击区只铺到那一层。 - 同组卡片保持相同宽度和内容节奏。
反模式
- 卡片嵌套卡片,两层边界互相削弱。
- 整卡可点的同时在正文里再放按钮或链接:它们被点击区盖住,点下去触发的是整卡。另有操作时放进脚部。
- 静态卡片也挂悬停反馈:没有 trigger 的卡片悬停抬起,读者会以为能点。
API 参考
产物
| 层 | 值 |
|---|---|
| 自定义元素 | <xh-card> |
| Vue 组件 | XhCardContent XhCardDescription XhCardFooter XhCardHeader XhCardRoot XhCardTitle XhCardTrigger |
| 状态机 | 无,connect 直接由 props 算属性 |
| 皮肤 | @xihan-ui/styles/card.css |
Props
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
interactive | boolean | 整卡可交互:标题里的 trigger(链接或按钮)把点击区铺满整张卡片,悬停抬起、按下换面, 焦点环沿卡片外沿画在卡片盒内。可及名只取 trigger 自己的文字,卡片里的其余内容不进链接名。 默认 false。 | |
variant | ControlVariant | 形态:outline 为带影的抬起面,subtle 为淡底,ghost 无底无影。默认 outline。 |
插槽
仅列出带载荷的插槽。
| Vue 组件 | 插槽 | 载荷 | 说明 |
|---|---|---|---|
XhCardRoot | default | — | |
XhCardRoot | title | — | |
XhCardRoot | description | — | |
XhCardRoot | content | — | |
XhCardRoot | footer | — |
React 适配器 props
只列各组件自己声明的那些:继承自 ComponentPropsWithRef 的 DOM 属性不在其中,根组件上与上面 Props 表同名的也不重复列。Vue 的对应物是上面的插槽表。
| React 组件 | 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
XhCardRoot | title | ReactNode | 标题。不给 children 时铺进页头的 title 部件。 | |
XhCardRoot | description | ReactNode | 标题下的说明。不给 children 时铺进页头的 description 部件。 | |
XhCardRoot | content | ReactNode | 正文。不给 children 时铺进 content 部件。 | |
XhCardRoot | footer | ReactNode | 页脚,通常放操作。不给 children 时铺进 footer 部件。 |
connect API
getXxxProps() 返回对应部件的宿主属性。
| 成员 | 类型 | 说明 |
|---|---|---|
getRootProps | () => T['element'] | |
getHeaderProps | () => T['element'] | |
getTitleProps | () => T['element'] | |
getTriggerProps | () => T['element'] | 整卡的触发器:放在 title 里的链接或按钮,interactive 时它的点击区铺满整张卡片。 |
getDescriptionProps | () => T['element'] | |
getContentProps | () => T['element'] | |
getFooterProps | () => T['element'] |
无障碍
键盘
规格出处:W3C APG
| 按键 | 生效条件 | 行为 |
|---|---|---|
Enter / Space | focus on trigger | 原生激活:链接只认 Enter 并跳转,按钮 Enter 与 Space 都认;整张卡片只有 trigger 这一个 Tab 位 |
- 可交互卡片的语义就是一个链接或按钮加一段普通内容:根不写 role、不拿焦点,读屏按文档序读到标题里的链接,再读说明与正文。
- trigger 必须有可见文字,它就是可及名;只放图标时补
aria-label。 - 点击区盖在内容之上,内容里的文字不能再拖选;需要复制的内容(编号、地址)不要放进可交互卡片,或放进脚部。
样式参考
皮肤
@xihan-ui/styles/card.css 按 [data-scope="card"][data-part="root"] 部件选择器书写,发布产物以挂载类 .xh-scope-card 代替其中的 data-scope(特异性相同),位于 xihan.components 层。覆盖样式使用 xihan.overrides,部件选择器照常可用。
forced-colors: active 下另有一套规则:颜色交给系统,边框与状态标记改用系统色关键字。
CSS 变量
本组件公开覆盖槽由独立皮肤的实际消费位生成;默认来源、作用部件和状态均与 CSS 同源。
| 变量 | 部件 | CSS 属性 | 状态 | 默认来源 | 说明 |
|---|---|---|---|---|---|
--xh-card-bg | root | background | defaultvariant=subtle | --xh-bg-subtle--xh-bg-surface | card 的 root 部件 background 覆盖槽。 |
--xh-card-bg-hover | root | background | hoverinteractivevariant=ghostvariant=subtle | --xh-_card-host-bg-hover--xh-bg-subtle | card 的 root 部件 background 覆盖槽。 |
--xh-card-bg-pressed | root | background | activeinteractivenot(:has([data-scope='card'][data-part='footer']:active)variant=ghostvariant=outlinevariant=subtle | --xh-_card-host-bg-pressed--xh-bg-subtle-hover--xh-bg-subtle-hover-opaque | card 的 root 部件 background 覆盖槽。 |
--xh-card-border | root | border | default | --xh-border-default | card 的 root 部件 border 覆盖槽。 |
--xh-card-content-fg | content | color | default | --xh-fg-muted | card 的 content 部件 color 覆盖槽。 |
--xh-card-content-font-size | content | font-size | default | --xh-text-body-size | card 的 content 部件 font-size 覆盖槽。 |
--xh-card-content-gap | content | gap | default | --xh-space-1 | card 的 content 部件 gap 覆盖槽。 |
--xh-card-description-fg | description | color | default | --xh-fg-muted | card 的 description 部件 color 覆盖槽。 |
--xh-card-description-font-size | description | font-size | default | --xh-text-secondary-size | card 的 description 部件 font-size 覆盖槽。 |
--xh-card-description-leading | description | line-height | default | --xh-leading-normal | card 的 description 部件 line-height 覆盖槽。 |
--xh-card-fg | root | color | default | --xh-fg-default | card 的 root 部件 color 覆盖槽。 |
--xh-card-font-size | root | font-size | default | --xh-text-label-size | card 的 root 部件 font-size 覆盖槽。 |
--xh-card-footer-gap | footer | gap | default | --xh-space-2 | card 的 footer 部件 gap 覆盖槽。 |
--xh-card-gap | root | gap | default | 0 | card 的 root 部件 gap 覆盖槽。 |
--xh-card-glint-duration | root | animation | @media (hover: hover) and (pointer: fine) and (forced-colors: none)hoverinteractivematerial=liquidwhere([data-material='liquid']) | --xh-motion-duration-glint | card 的 root 部件 animation 覆盖槽。 |
--xh-card-header-border | headerroot | border-block-end | default | --xh-border-subtle | card 的 header、root 部件 border-block-end 覆盖槽。 |
--xh-card-header-h | headerroot | min-block-size | default | --xh-surface-header-h | card 的 header、root 部件 min-block-size 覆盖槽。 |
--xh-card-header-py | headerroot | padding-block | default | --xh-space-2 | card 的 header、root 部件 padding-block 覆盖槽。 |
--xh-card-leading | root | line-height | default | --xh-text-body-leading | card 的 root 部件 line-height 覆盖槽。 |
--xh-card-p | contentfooterheaderroot | paddingpadding-block-endpadding-inline | default | --xh-space-4--xh-surface-pad-lg | card 的 content、footer、header、root 部件 padding、padding-block-end、padding-inline 覆盖槽。 |
--xh-card-radius | roottrigger | border-radius | defaultinteractive | --xh-shape-surface | card 的 root、trigger 部件 border-radius 覆盖槽。 |
--xh-card-shadow | root | box-shadow | defaultvariant=ghostvariant=subtle | --xh-elevation-raisednone | card 的 root 部件 box-shadow 覆盖槽。 |
--xh-card-shadow-hover | root | box-shadow | hoverinteractivevariant=outline | --xh-elevation-lifted | card 的 root 部件 box-shadow 覆盖槽。 |
--xh-card-title-fg | title | color | default | --xh-fg-default | card 的 title 部件 color 覆盖槽。 |
--xh-card-title-font-size | title | font-size | default | --xh-text-heading-3-size | card 的 title 部件 font-size 覆盖槽。 |
--xh-card-title-font-weight | title | font-weight | default | --xh-text-heading-3-weight | card 的 title 部件 font-weight 覆盖槽。 |
--xh-card-title-leading | title | line-height | default | --xh-leading-tight | card 的 title 部件 line-height 覆盖槽。 |
动效
动效角色:按压 · 状态(见动效规范)。
可覆盖的动效槽:--xh-card-glint-duration。
background-color · box-shadow 走 transition 过渡。时长与缓动读动效令牌,改令牌即改全局节奏。
系统开启减弱动效时由令牌层统一收敛,皮肤不另作判断。
响应式
皮肤另按输入能力分档:hover: hover:同一份皮肤在触屏与带指针的设备上不一样,与视口宽度无关。
RTL
皮肤用逻辑属性排布(inline-start 一族),dir="rtl" 下自动镜像;只认物理方向的量乘 --xh-direction-sign 换向,按就近的 dir 走。
