← 中文资源

简体中文版本

CSS Grid auto-fit 图片画廊

用 repeat(auto-fit, minmax(min(100%, 14rem), 1fr)) 构建不溢出、等宽自适应且标题对齐的图片网格。

阅读英文原文

功能说明

画廊使用无序列表承载卡片,网格列由 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;
  }
}