做组件库的同学一定遇到过这个场景:同一张卡片,放在主内容区里舒展大气,一丢进侧边栏就挤成一团。你用媒体查询写了三档响应式,结果组件换个地方放就全乱套——因为媒体查询只能看见视口,看不见组件所处的容器

组件的响应式理应由容器决定,而不是由屏幕决定。这就是容器查询(Container Queries)要解决的问题。2023 年起它已在所有主流浏览器落地,如今是我在组件库里默认使用的方案。

20 秒上手

总共就两步:先在父元素上声明容器,再按容器尺寸写样式。

/* 第一步:声明容器 */
.card-wrap {
  container-type: inline-size;
  container-name: card;
}

/* 第二步:按容器宽度写样式 */
@container card (min-width: 480px) {
  .card { display: flex; gap: 16px; }
  .card img { width: 160px; }
}

container-name 可以省略,省略后 @container (min-width: 480px) 会匹配最近的容器祖先。

实战一:文章卡片的三段式布局

本站的首页文章卡片就是典型例子:窄容器纵向排列,宽容器图文横排,再宽一点摘要显示两行。三个断点全部基于卡片自身宽度:

.post-wrap { container-type: inline-size; }

@container (max-width: 419px) {
  .post-card img { width: 100%; aspect-ratio: 16/9; }
}

@container (min-width: 420px) {
  .post-card { display: grid; grid-template-columns: 120px 1fr; }
}

@container (min-width: 640px) {
  .post-card { grid-template-columns: 200px 1fr; }
  .post-card .excerpt {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
}

把这张卡片放进主栏、侧栏还是弹窗,布局都由容器说了算,组件从此可以放心到处搬。

实战二:表单标签自适应宽度

后台系统里表单经常被拖来拖去。用容器查询让 label 在窄容器里换到输入框上方:

.field-wrap { container-type: inline-size; }

.field { display: flex; align-items: center; gap: 12px; }
.field label { flex: 0 0 96px; text-align: right; }

@container (max-width: 480px) {
  .field { flex-direction: column; align-items: stretch; }
  .field label { text-align: left; }
}

实战三:cqw,容器里的「rem」

容器查询单位常被忽略:cqw / cqi 是容器宽度的 1%,cqh / cqb 对应高度。组件标题想随容器等比缩放,再也不用 JS:

.widget h3 {
  /* 标题始终约为容器宽度的 6%,且被夹在 16px ~ 28px 之间 */
  font-size: clamp(16px, 6cqi, 28px);
}

老浏览器的降级方案

.card { display: block; }

@supports (container-type: inline-size) {
  .card-wrap { container-type: inline-size; }
  @container (min-width: 480px) {
    .card { display: flex; }
  }
}

思路是「先写一个能用的默认布局,再用 @supports 渐进增强」。Safari 16 之前的份额如今已经很少,多数项目甚至可以不做降级。

三个容易踩的坑

  1. 容器不能查询自己@container 里的样式要写在容器的子元素上,写在声明容器的那一层是无效的;
  2. container-type: inline-size 会应用布局、样式和行内尺寸的包含(containment),同时建立新的包含块——子元素的 position: fixed 会改为相对容器定位,弹层组件要留意;
  3. 容器查询查的是尺寸,目前也支持 style() 查询(如根据容器颜色变量切换样式),但浏览器支持仍在推进中,生产环境慎用。

写在最后

媒体查询负责「页面级」布局,容器查询负责「组件级」布局,两者配合才是完整的响应式体系。把组件的断点还给组件本身,你会发现 UI 代码突然清爽了很多。