Tokyo Night
深色主题的基准:凹槽最深,面板抬一层,越靠交互越亮。
卡片是页面里最小的"面板"。它的浮起不是投影,而是 box-shadow: var(--zz-elevation)给的 1px 零模糊描边;悬停只换 --zz-elevation-hover(同一几何、只改 alpha), 所以不会位移、也不会跳动。同族的徽章、键帽、头像、统计值共用这套面与线的规则。
媒体井是 16:9 的凹槽:--zz-bg-deep 底 + outline: 1px solid var(--zz-img-outline)(outline-offset: -1px,内描边不占布局)。内容区固定 1rem 内距、0.375rem 行距。标题 0.875rem sans 500 截断,描述 13px,元信息 0.75rem mono。
变体可以叠加:--plain 关掉媒体井、--tight 把内距收到 0.75rem、--interactive 悬停换 elevation、--deep 换成凹槽底。 传 href 的卡片整张都是热区,焦点环内缩 2px 画在卡片边缘。
没有媒体井,内容区仍是 1rem 内距。
内距收到 0.75rem,行距 0.25rem,适合密集列表。
悬停把描边换成 elevation-hover,整卡可点。
凹槽底,用在本身已经抬起的面板里面。
徽章 1.25rem 高、0.75rem mono;有底变体统一用 12% 的状态色底 + 状态色文字,带状态点时前置 0.375rem 圆点。键帽靠底部加厚 1px 描边表达"键",没有投影。头像圆形的例外只此一处。
---
import Card from '@/components/ui/Card.astro'
import Badge from '@/components/ui/Badge.astro'
---
<Card title="Tokyo Night" description="深色主题的基准。" meta="theme · tokyo-night" href="/themes">
<img slot="media" src="/themes/tokyo-night.png" alt="Tokyo Night 预览" />
<Fragment slot="actions">
<Button variant="outline" size="xs">复制</Button>
</Fragment>
</Card>
<Card variant={['interactive', 'tight']} title="紧凑可点" href="/docs" />
<Card variant="plain" title="无媒体" description="内容区仍是 1rem 内距。" />
<Badge variant="success" dot>在线</Badge>
<Kbd>K</Kbd>
<Avatar size="lg" initials="zz" alt="zz" />
<Stat value="0" unit="px" label="圆角半径" />| 类名 | 作用 |
|---|---|
.zz-card | 面板底 --zz-surface + box-shadow: var(--zz-elevation),overflow: hidden 裁住媒体井 |
.zz-card__media | 16:9 媒体井:--zz-bg-deep 底 + --zz-img-outline 的 1px 内描边 |
.zz-card__body | 内容区:1rem 内距 + 0.375rem 行距,纵向撑满卡片 |
.zz-card__title | 0.875rem sans 500、单行截断,色 --zz-text |
.zz-card__desc | 13px / 1.6,色 --zz-text-secondary,最多两行 |
.zz-card__meta | 0.75rem mono --zz-text-muted + tabular-nums |
.zz-card__actions | 卡内操作区:position: relative,靠 DOM 顺序(在热区之后)拿到点击,并被推到卡底;不引入 z-index |
.zz-card__link | 标题里的链接,继承文字色、去下划线 |
.zz-card__hotzone | position: absolute; inset: 0,把点击面铺满整卡 |
.zz-card__ring | 焦点环载体:outline-offset: -2px,避免被卡片自身的 overflow: hidden 裁掉 |
.zz-card--plain | --tight | --interactive | --deep | 无媒体 / 0.75rem 内距 / 悬停换 --zz-elevation-hover / 凹槽底 |
.zz-badge + --brand | --danger | --warning | --success | --info | --muted | 1.25rem 高的标签;基础态无底 + --zz-border-subtle 描边,有底变体走 12% 状态色 |
.zz-badge__dot | 0.375rem 状态点,取 currentColor,随变体自动换色 |
.zz-kbd | --zz-border-strong 描边 + 底部加厚到 2px,0.6875rem,底 --zz-surface-2 |
.zz-avatar + --sm | --md | --lg | --xl | 1.5 / 2 / 2.5 / 3rem 圆形,底 --zz-surface-2,描边 --zz-border-subtle |
.zz-stat + --lg | 统计值:1.75 / 2.25rem sans 600 + tabular-nums,单位与标签 0.75rem mono |
<a>,所以读屏与键盘用户拿到的就是一个有名字的链接;点击面由 aria-hidden 的覆盖层负责,不产生重复的可聚焦元素。.zz-card__ring 上;卡片 overflow: hidden,外扩的环会被裁掉,所以这里不能用全局的 outline-offset: 2px。actions 槽,否则会被热区盖住点不到;热区是覆盖层,不会改变 DOM 顺序。role="img" + aria-label 提供名字,首字母本身对读屏隐藏;有图片时 alt 直接透传,纯装饰头像请传 alt=""。--danger / --success 都配了文字,状态点只是加速扫读。