Card 卡片

承载一组内容的容器,比页面底色高出一层。

サクラノ詩

连载中

转学第一天,我在天台遇见了那个抱着旧相机的少女。

全 12 卷

<script setup lang="ts">
  import { Button, Card, Divider, Heading, Inline, Stack, Tag, Text } from '@hina-ui/vue'
</script>

<template>
  <Card class="w-full max-w-sm">
    <Stack>
      <Stack gap="xs">
        <Inline justify="between" align="start" :wrap="false">
          <Heading :level="3" size="md">サクラノ詩</Heading>
          <Tag tone="success">连载中</Tag>
        </Inline>
        <Text tone="muted" size="sm">转学第一天,我在天台遇见了那个抱着旧相机的少女。</Text>
      </Stack>
      <Divider />
      <Inline justify="between">
        <Text tone="faint" size="sm">全 12 卷</Text>
        <Button variant="ghost" tone="neutral" size="sm">查看</Button>
      </Inline>
    </Stack>
  </Card>
</template>
vue

用法

import { Card } from '@hina-ui/vue'
ts

卡片由三部分构成视觉:自身的背景色、一条细边框,以及一层静止状态的阴影,三者共同使它比页面底色高出一层。内容置于默认插槽中。

卡片自带背景色、一条细边框与一层阴影,内容置于默认插槽中。

<script setup lang="ts">
  import { Card, Text } from '@hina-ui/vue'
</script>

<template>
  <Card class="w-full max-w-sm">
    <Text>卡片自带背景色、一条细边框与一层阴影,内容置于默认插槽中。</Text>
  </Card>
</template>
vue

卡片只负责这一层容器,不决定其中内容如何排布。内容的方向与间距应交由 Stack、Inline 等布局组件处理。

示例

内边距

卡片默认带有内边距,取值随密度变化。图片、表格等需要贴合卡片边缘的内容,应将 padded 设为 false,再由内部容器提供内边距。此时卡片会裁去溢出的部分,贴边内容的直角不会盖住卡片的圆角。

夏日午后的坡道

通向车站的那条坡道

图片贴合卡片边缘,内边距改由内部容器提供。

<script setup lang="ts">
  import { Card, Heading, Image, Stack, Text } from '@hina-ui/vue'
</script>

<template>
  <Card :padded="false" class="w-full max-w-sm overflow-hidden">
    <Image src="/sample.webp" alt="夏日午后的坡道" class="h-32 w-full" />
    <Stack gap="xs" class="p-[var(--hn-panel-p)]">
      <Heading :level="3" size="base">通向车站的那条坡道</Heading>
      <Text tone="muted" size="sm">图片贴合卡片边缘,内边距改由内部容器提供。</Text>
    </Stack>
  </Card>
</template>
vue

若内容需要随圆角一同裁切,为卡片添加 overflow-hidden。

语义标签

as 用于指定渲染的标签,默认为 div,仅在需要更准确的语义时替换:独立成篇的内容使用 article,页面中的一个区块使用 section;一组卡片构成列表时,容器使用 ul,每张卡片使用 li。

独立成篇、可单独发布的内容使用 article

页面中的一个区块使用 section

<script setup lang="ts">
  import { Card, Stack, Text } from '@hina-ui/vue'
</script>

<template>
  <Stack class="w-full max-w-sm">
    <Card as="article">
      <Text>独立成篇、可单独发布的内容使用 article</Text>
    </Card>
    <Card as="section">
      <Text>页面中的一个区块使用 section</Text>
    </Card>
  </Stack>
</template>
vue

可点击的卡片

卡片自身不带任何可点击的样式,既不会在悬停时改变颜色,也不会提升阴影层级。需要整张卡片可点击时,由调用方组合实现:as="button" 替换标签,hn-interactive 提供指针样式与焦点环,hn-state-layer 提供悬停时的状态层,hn-press-lg 将按压时的缩放幅度调整为适合大面积元素的档位,另置入一个 Ripple。

<script setup lang="ts">
  import { Card, Heading, Ripple, Stack, Text } from '@hina-ui/vue'
</script>

<template>
  <Card as="button" class="hn-interactive hn-state-layer hn-press-lg w-full max-w-sm text-start">
    <Ripple />
    <Stack gap="xs">
      <Heading :level="3" size="base">整张卡片都是可点击区域</Heading>
      <Text tone="muted" size="sm">悬停时的状态层与按下时的波纹均由调用方添加。</Text>
    </Stack>
  </Card>
</template>
vue

若卡片中仅有局部可点击,则不应如此处理:应在该处放置 Button 或 Link,卡片本身保持静态。

密度

内边距取自密度 token,因此在任意祖先元素上设置 data-density="compact",其范围内的卡片都会随之收紧,无需向卡片传入参数。

comfortable

内边距 20px

compact

内边距 14px

<script setup lang="ts">
  import { Card, Inline, Stack, Text } from '@hina-ui/vue'
</script>

<template>
  <Inline align="start" class="w-full max-w-lg">
    <Card class="flex-1">
      <Stack gap="xs">
        <Text size="sm" tone="faint">comfortable</Text>
        <Text>内边距 20px</Text>
      </Stack>
    </Card>
    <Card data-density="compact" class="flex-1">
      <Stack gap="xs">
        <Text size="sm" tone="faint">compact</Text>
        <Text>内边距 14px</Text>
      </Stack>
    </Card>
  </Inline>
</template>
vue

无障碍

  • 卡片默认渲染为 div,不带角色,屏幕阅读器按普通内容朗读。
  • 整张卡片可点击时必须替换为 button 或链接。不应为 div 绑定点击事件,那样键盘无法触达,屏幕阅读器也无法识别其为控件。
  • 卡片内的标题使用 Heading,并选择恰当的 level,使其衔接页面已有的标题层级。

API

Props

属性
类型
默认值
说明
padded
boolean
true
是否带内边距,取值随密度变化;关闭时裁去溢出
as
string
'div'
渲染的标签
as-child
boolean
false
不渲染自身标签,把样式合并到唯一的子元素
class
string
—
追加至根元素的类名

Slots

插槽
说明
default
卡片的内容