卡片

卡片把相关内容归拢到自己的一块面上。它的各部分——cardHeader()、cardMedia()、cardBody()、cardFooter()——是各自独立的函数而不是属性,所以你只用得着的那几个,并按设计想要的顺序摆放。

基础卡片

基于文件的路由

src/about.ht.js → /about

目录变成路径,方括号变成参数。没有路由器要配置。

card(
  cardHeader({ title: '基于文件的路由', subtitle: 'src/about.ht.js → /about' }),
  cardBody(text({ variant: 'small', tone: 'muted' }, '目录变成路径,方括号变成参数。没有路由器要配置。')),
)

变体

outlined 是默认值。elevated 用阴影换掉边框,flat 则两者都不要,改为给面上色。

Outlined

Elevated

Flat

grid({ min: '13rem' },
  card({ variant: 'outlined' }, cardBody(text({ variant: 'small' }, 'Outlined'))),
  card({ variant: 'elevated' }, cardBody(text({ variant: 'small' }, 'Elevated'))),
  card({ variant: 'flat' }, cardBody(text({ variant: 'small' }, 'Flat'))),
)

带页脚

divided 会在页脚上方加一条细线。页脚会被推到底部,所以并排的卡片即使上方文字长短不一,操作也能对齐。

基础站点

一个最小项目,外加部署配置。

Markdown 博客

一个装着 .md 文件的目录,渲染成静态页面,带 RSS 订阅,完全没有客户端 JavaScript。

grid({ min: '14rem' },
  card(
    cardHeader({ title: '基础站点' }),
    cardBody(text({ variant: 'small', tone: 'muted' }, '一个最小项目,外加部署配置。')),
    cardFooter({ divided: true }, button({ size: 'sm', variant: 'soft' }, '打开')),
  ),
  card(
    cardHeader({ title: 'Markdown 博客' }),
    cardBody(text({ variant: 'small', tone: 'muted' }, '一个装着 .md 文件的目录,渲染成静态页面,带 RSS 订阅,完全没有客户端 JavaScript。')),
    cardFooter({ divided: true }, button({ size: 'sm', variant: 'soft' }, '打开')),
  ),
)

媒体

cardMedia() 以固定宽高比填满卡片顶部,所以不管源图多大,一排卡片都能保持整齐。

默认 16 / 9

ratio: 4 / 3

grid({ min: '13rem' },
  card(
    cardMedia({ src: '/logo.svg', alt: '', style: 'background: var(--su-surface-2); object-fit: contain; padding: 1rem' }),
    cardBody(text({ variant: 'small' }, '默认 16 / 9')),
  ),
  card(
    cardMedia({ src: '/logo.svg', alt: '', ratio: '4 / 3', style: 'background: var(--su-surface-2); object-fit: contain; padding: 1rem' }),
    cardBody(text({ variant: 'small' }, 'ratio: 4 / 3')),
  ),
)

整张卡片作为链接

给卡片一个 href,整块面就成了一个链接,并附带悬停上浮效果。这种形态下别在卡片里再放按钮或其他链接——可交互内容不能嵌在链接里面。改用普通卡片加一个页脚按钮。

grid({ min: '14rem' },
  card({ href: '/zh/docs/routing' },
    cardHeader({ title: '路由', subtitle: '阅读指南' }),
    cardBody(text({ variant: 'small', tone: 'muted' }, '动态路由、通配路由和路由分组。')),
  ),
  card({ href: '/zh/docs/data' },
    cardHeader({ title: '数据加载', subtitle: '阅读指南' }),
    cardBody(text({ variant: 'small', tone: 'muted' }, 'data() 在构建时运行,并带 fetch 缓存。')),
  ),
)

内边距

一个属性就能一次设定卡片所有部分的内边距。

padding: sm

padding: xl

stack({ gap: 'md' },
  card({ padding: 'sm' }, cardBody(text({ variant: 'small' }, 'padding: sm'))),
  card({ padding: 'xl' }, cardBody(text({ variant: 'small' }, 'padding: xl'))),
)

自由内容

这些部件是方便,而不是硬性要求——卡片接受任意子元素,cardHeader() 也能在标题旁接受自己的子元素,比如右侧放一个头像或菜单按钮。

Paul Browne

4 分钟前部署

PB
构建通过12 个页面4.1 kB
card(
  cardHeader(
    { title: 'Paul Browne', subtitle: '4 分钟前部署' },
    avatar({ name: 'Paul Browne', size: 'sm' }),
  ),
  cardBody(
    stack({ direction: 'row', gap: 'sm', wrap: true },
      chip({ color: 'success', dot: true }, '构建通过'),
      chip({ color: 'neutral' }, '12 个页面'),
      chip({ color: 'neutral' }, '4.1 kB'),
    ),
  ),
)

属性

card():

属性类型默认值说明
variant'outlined' | 'elevated' | 'flat''outlined'这块面如何与页面区分开。
hrefstring—把整张卡片渲染成链接。
paddingSpace'lg'卡片各部分统一使用的内边距。

各部件:

部件属性默认值说明
cardHeadertitle, subtitle—标题和副标题,以及并排放置的任意子元素。
cardTitleas'h3'单独的标题,用于手写头部时。
cardSubtitle—标题下方那行弱化文字。
cardMediasrc, alt, ratio'16 / 9'固定宽高比的封面图。
cardBody—主内容区域。
cardFooterdividedfalse底部的操作行;divided 会在其上方加细线。