概览
一张卡片可将一个相关内容或一个选项归为一组。它是一种视觉模式,而非固定的语义或互动模型,因此请保留内容的含义,并使用与其用途相符的原生互动。
指南
分别为内容及其互动选择语义:
- 使用
<article>作为卡片根,无论它是否链接到目标页面; - 使用原生链接进行导航。如果整个卡片是一个目标页面,则只有在卡片不包含其他互动元素(
a、button、input、select、textarea)时,单个链接才能封装<article>; - 将用于独立操作的链接和按钮保留为单独的原生控件;
- 如果卡片是选项,请使用带有相关
<label>的单选按钮或复选框,或在原生<select>中使用option。
媒体、标题、辅助内容和操作应保持有意义的来源顺序;请勿使用 CSS order 与之相悖(请参阅 CSS 布局和自适应设计)。
实现
尽量减少标记,让 CSS 选择器识别每个区域,这样既能保持简单卡片的简洁性,又能实现更丰富的复合信息卡。一张卡片最多有四个区域,按此来源顺序排列,其中任何一个区域都可以省略。区域是卡片的直接子级,未分组到 .card__body 等封装容器元素中,这会阻止通过选择器放置区域和设置区域样式。如果一个区域只有一个元素,则该区域就是该元素本身,不进行封装:一个单独的段落就是 <p>。仅当一个区域包含多个元素时,才使用封装容器。
- 媒体:
<img>、<picture>、<video>或<svg>作为第一个子项。仅向不属于这些元素的任意主打内容添加.media类,切勿将普通<img>封装在.media元素中。 - 标题:单独的标题。仅当标题包含副标题或宣传口号时,才使用
<hgroup>;当区域包含标题以外的内容(例如署名或徽章)时,才使用<header>。如果卡片有主要目标页面,链接会放在标题内。 - 内容:单个
<p>、<ul>或<dl>独立存在。如果内容包含多个元素(例如多个段落或一个段落加一个列表),请将它们归入一个<div>中,以便该区域成为单个网格项。 - Footer:一个
<footer>,其中包含作为原生链接和按钮的独立操作。“阅读更多”链接可以与标题链接共用网址,但应使用视觉上隐藏的文字(例如Read more<span> about poached eggs</span>)为其提供一个以可见文字开头的描述性无障碍名称,因为相同的“阅读更多”链接在屏幕阅读器的链接列表中会造成混淆。请勿添加重复标题网址的其他页脚链接。
最简单的卡片是包含标题和段落的 <article>。选择与卡片行为匹配的链接和操作模式;请参阅互动模型。
对于应填充固定比例区域的卡片图片或视频,请在 <img> 或 <video> 本身上设置 aspect-ratio 和 object-fit: cover,使其填充该区域而不会失真(可能会发生剪裁)。仅将 .media 容器的 aspect-ratio 用于其他强推内容。否则,请保留媒体的固有比例。为说明性图片提供适当的替代文本;为装饰性图片使用 alt=""。
适应可用空间
根据卡片的可用空间(而非视口)来设计布局。如需调整卡片的后代(媒体、文本、操作),请将卡片设为尺寸容器 (container-type: inline-size),并针对其子项编写 @container 规则。避免根据网格或列表父级调整大小:卡片的实际宽度可能取决于列数、间距等,因此基于父级的断点会产生干扰。
在狭窄的容器中,按来源顺序堆叠区域。在宽容器中,将媒体内容放置在其他区域旁边的侧边栏中,但仅适用于包含媒体内容的卡片:在媒体子级(例如 .card:has(> img))上使用 :has() 限定这些规则,以便列定义和 grid-column 放置位置永远不会应用于不含媒体内容的卡片。不含媒体内容的卡片在任何宽度下都保持单列布局,且不得预留空的媒体列。
在网格中对齐卡片
如果一行中的卡片应共享其区域的高度,请使用 subgrid:让每张卡片跨越四个共享行轨道(媒体、标题、内容、页脚),并按元素选择器放置区域,将非媒体、标题或 <footer> 的所有内容视为内容。如果某张卡片省略了某个区域,则该区域对应的轨道会留空,因此其余区域仍会与相邻卡片对齐。每个轨道只能容纳一个网格项,因此只有在内容区域包含多个元素且卡片共享轨道时,才应将内容区域封装在 <div> 中。未与同级元素对齐的卡片既不需要封装容器,也不需要子网格。
互动模型
内容卡片
使用 <article> 作为卡片根。将链接和按钮作为单独的原生控件。
单目的地卡片
如果整个卡片是一个目标页面,且没有独立操作,请使用一个原生链接。链接可以封装 <article>。请勿在链接中嵌套其他链接、按钮或表单控件,并确保所有“阅读更多”文本均为纯 <p> 或 <span>。由于链接的无障碍名称是卡片的所有文本,因此请保持内容简短;如果卡片包含大量文本或其他操作,请使用下面的拉伸链接模式,以便只有标题是链接:
<a href="/recipes/poached-eggs">
<article class="card">
<h3>Poached eggs</h3>
<p>Two poached eggs served on toasted sourdough.</p>
</article>
</a>
可点击的卡片,支持独立操作
如果点击卡片会触发操作而不是导航,请使用 <button>,而不是链接。
当内容卡片链接到主要目标页面,同时包含其他互动元素,并且需要整体可点击时:
- 在主链接本身上使用单次选择启用:在每张卡片的一个链接(标题链接)上放置
.card-link。页脚和其他链接永远不会获得焦点,否则每个链接都会拉伸自己的叠加层,而最后一个链接会捕获整个卡片。 - 使用
::after链接其标题,并将指针点击区域延伸到整个卡片
.card:has(a.card-link) {
position: relative;
}
.card a.card-link::after {
content: "";
position: absolute;
inset: 0;
}
- 使用
position: relative; z-index: 1提升互动元素,使其保持可操作状态。定位到元素类型(例如a, button),而不是要求通过类选择启用。 - 请勿将
z-index应用于整个容器,因为这会使容器捕获不会产生任何操作的指针事件。
.card:has(a.card-link) {
/* Keep other interactive elements above the overlay */
a:not(.card-link), button, input, x-button {
position: relative;
z-index: 1;
}
}
- 在悬停/聚焦时为卡片本身设置样式,而不是为拉伸的链接设置样式:
.card {
a.card-link {
&:focus-visible {
outline: none; /* Draw it on the card instead */
}
}
&:has(a.card-link:focus-visible) {
/* Focus ring */
}
&:has(a.card-link:hover) {
/* Hover styles */
}
}
选项卡
对于表示预定义选项的卡片,请使用 <label class="card">,并将复选框或单选按钮作为直接子级。
使用 :has(:checked) 为所选卡片设置样式。切勿在选择时更改会影响布局的属性(例如 border-width、padding 或 font-weight),因为卡片及其相邻元素会跳动;请使用 outline、box-shadow 或仅更改颜色的透明边框来突出显示:
.card {
border: 2px solid transparent;
&:has(> :checked) {
border-color: var(--accent);
}
}
可见的复选框或单选按钮本身就是一种非颜色选择指示器,因此仅改变颜色就足够了。如果控件在视觉上处于隐藏状态(使其可聚焦且位于无障碍树中,绝不使用 display: none),请添加不影响布局的非颜色指示器,例如对勾图标或更粗的 outline。
如果选项属于下拉选择器,且选项以卡片形式布局,请使用原生 <select> 搭配 appearance: base-select,并设置 ::picker(select) 和 <option> 元素的样式,而不是将选择器替换为 <div> 卡片;如需了解更多详情和后备方案,请参阅自定义选择器布局。