功能说明
画廊使用无序列表承载卡片,网格列由 auto-fit 与 minmax 自动决定:容器够宽时尽量多排,空间不足时减少列数,剩余空间由 1fr 均分。min(100%, 14rem) 保证单列在极窄容器中也不会横向溢出。卡片内部用三行网格组织图片、标题和描述,并通过 subgrid 让不同卡片的标题与描述在行方向上对齐。图片使用固定宽高比,加载前后不跳动。
关键代码
repeat(auto-fit, minmax(min(100%, var(–gallery-min)), 1fr)) 是核心:变量控制最小卡片宽度,min 处理窄于最小宽度的容器。gap 使用 clamp 在 0.75rem 到 1.5rem 之间随视口变化。min-inline-size: 0 允许网格项收缩,避免长单词撑破列。subgrid 分支让每张卡片跨三行并继承父网格的行轨道;不支持时保留普通三行布局。图片使用 aspect-ratio 预留空间,避免加载时布局抖动。
使用注意
auto-fit 会折叠空轨道,auto-fill 则保留空列,选择哪种取决于希望最后一行如何分布。卡片数量很少时 auto-fit 会把卡片拉得很宽,可用最大宽度或固定列数约束。把列表样式设为 none 后,Safari 可能移除列表语义,必要时给 ul 加 role=“list”。subgrid 需要父网格显式行轨道,当前父级只定义列,浏览器会按内容生成行,效果需实测。图片仍应设置 width/height 或 srcset。
验证方式
在 320px 到 1600px 之间连续调整窗口,确认没有横向滚动条,列数逐步变化且卡片等宽。放两张和三张卡片,比较 auto-fit 与 auto-fill 的末行拉伸差异。检查不同长度标题的卡片底部是否对齐;在不支持 subgrid 的浏览器中应正常降级为各自高度。
代码示例
/* Markup contract:
<ul class="gallery">
<li class="gallery__item">
<img src="/cabin.jpg" alt="Cabin at dusk">
<h3>Cabin at dusk</h3>
<p>Two-night stay near the lake.</p>
</li>
</ul> */
.gallery {
--gallery-min: 14rem;
--gallery-gap: clamp(0.75rem, 2vw, 1.5rem);
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, var(--gallery-min)), 1fr));
gap: var(--gallery-gap);
align-items: start;
margin: 0;
padding: 0;
list-style: none;
}
.gallery__item {
display: grid;
grid-template-rows: auto 1fr auto;
gap: var(--space-2, 0.5rem);
min-inline-size: 0;
padding: var(--space-3, 1rem);
border: 1px solid var(--color-border, #d4d4d8);
border-radius: var(--radius-md, 0.5rem);
background: var(--color-surface, #ffffff);
}
.gallery__item img {
inline-size: 100%;
aspect-ratio: 4 / 3;
object-fit: cover;
border-radius: calc(var(--radius-md, 0.5rem) - 0.125rem);
}
.gallery__item h3 {
margin: 0;
font-size: 1.05rem;
text-wrap: balance;
}
.gallery__item p {
margin: 0;
color: var(--color-text-muted, #52525b);
text-wrap: pretty;
}
/* Keep every card the same height when captions wrap to different lines. */
@supports (grid-template-rows: subgrid) {
.gallery__item {
grid-row: span 3;
grid-template-rows: subgrid;
}
}