CSS 容器查询

深入理解 CSS 容器查询的工作机制、@container 规则、容器类型(size、inline-size、style、state),以及如何用容器查询构建真正的组件级响应式设计。

CSS 容器查询

容器查询解决的是什么问题

传统的响应式设计基于视口(viewport)来调整布局。当视口宽度小于某个阈值时,整页布局发生变化。但这种方法是页面级别的,不是组件级别的。

想象一个卡片组件:它在页面主内容区显示时是宽的,在侧边栏显示时是窄的,在弹窗中显示时又不同。传统的媒体查询无法让同一个组件在不同容器中自动适应——除非你为每种容器写不同的 class。

容器查询让组件能够根据自身容器(而不是视口)的尺寸来调整样式,实现了真正的组件级响应式。

面试定位:容器查询是 CSS 的重大新特性(2023+ 才在主流浏览器稳定支持)。面试官通过候选人对容器查询机制的理解和应用场景的判断,问的是对新 CSS 特性的掌握程度。


容器查询的基础语法

1. 定义容器

/* 定义一个容器 */
.card-container {
  container-type: inline-size; /* 监听行内轴尺寸变化 */
  container-name: card;         /* 可选:命名容器 */
}

/* 简写 */
.card-container {
  container: card / inline-size; /* name / type */
}

2. 使用 @container 查询

/* 根据容器宽度查询 */
@container card (min-width: 400px) {
  .card {
    flex-direction: row;
  }

  .card-image {
    width: 50%;
  }
}

/* 无命名容器的查询 */
@container (min-width: 300px) {
  .card {
    padding: 1.5rem;
  }
}

container-type 的类型

.container {
  /* size:容器成为块级容器,查询两个轴 */
  container-type: size;

  /* inline-size:只查询行内轴(常用) */
  container-type: inline-size;

  /* style:查询样式相关的容器状态 */
  container-type: style;

  /* state:查询容器状态(如悬停) */
  container-type: state;
}

为什么常用 inline-size 而不是 size

/* size 会限制内部布局,可能导致意外 */
.size-container {
  container-type: size;
  height: 300px; /* 必须设置高度 */
}

.size-container {
  container-type: inline-size; /* 更灵活,不限制块轴 */
}

实战:卡片组件的容器查询

基础结构

<article class="card">
  <div class="card-image">
    <img src="photo.jpg" alt="风景照">
  </div>
  <div class="card-content">
    <h2>标题</h2>
    <p>描述文本...</p>
    <button>操作按钮</button>
  </div>
</article>

容器查询样式

/* 1. 定义容器 */
.card-wrapper {
  container: card / inline-size;
}

/* 2. 默认样式(小容器) */
.card {
  display: flex;
  flex-direction: column;
}

.card-image img {
  width: 100%;
  aspect-ratio: 16/9;
  object-fit: cover;
}

.card-content {
  padding: 1rem;
}

/* 3. 容器宽度 >= 400px 时 */
@container card (min-width: 400px) {
  .card {
    flex-direction: row;
  }

  .card-image {
    width: 40%;
  }

  .card-image img {
    width: 100%;
    height: 100%;
    aspect-ratio: auto;
  }

  .card-content {
    padding: 1.5rem;
    flex: 1;
  }
}

/* 4. 容器宽度 >= 600px 时(大屏布局) */
@container card (min-width: 600px) {
  .card {
    gap: 2rem;
  }

  .card-image {
    width: 50%;
  }

  .card-content h2 {
    font-size: 1.5rem;
  }
}

容器查询与媒体查询的对比

特性 容器查询 媒体查询
查询对象 组件容器 视口
组件自治 组件自己感知容器 需要外部 class 控制
复用性 高(同一组件多处使用) 低(依赖页面上下文)
浏览器支持 2023+ 稳定支持 全部支持
/* 媒体查询:组件在不同位置需要不同的 class */
.sidebar .card { /* ... */ }
.main .card { /* ... */ }
.modal .card { /* ... */ }

/* 容器查询:组件自己感知容器 */
.card-wrapper { container: card / inline-size; }
@container card (min-width: 400px) { /* ... */ }

容器查询的单位

容器查询引入了新的相对单位:

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

/* cqi = 容器行内尺寸的 1% */
.cqi-unit {
  font-size: 5cqi; /* 容器宽度的 5% */
}

/* cqb = 容器块轴尺寸的 1% */
.cqb-unit {
  font-size: 5cqb; /* 容器高度的 5% */
}

/* cqw = 容器宽度的 1% */
.cqw-unit {
  width: 20cqw;
}

/* cqh = 容器高度的 1% */
.cqh-unit {
  height: 10cqh;
}

/* cqmin = cqw 和 cqh 中较小的 */
.cqmin-unit {
  padding: 2cqmin;
}

面试高频问题

Q: 容器查询和媒体查询有什么区别?

回答要点:媒体查询基于视口尺寸来调整样式,容器查询基于组件所在容器的尺寸。容器查询实现了组件级别的响应式——同一个组件在不同容器中可以自动适应,不需要外部 class 控制。媒体查询适合页面级布局,容器查询适合组件级布局。

Q: 为什么 container-type: inline-size 比 size 更常用?

回答要点size 类型会限制容器的块轴尺寸(通常需要设置 height),这可能限制内部布局的灵活性。inline-size 只监听行内轴(通常是宽度),不限制块轴,更适合大多数组件的响应式需求。

Q: 容器查询有哪些新单位?

回答要点:容器查询引入了 cqi(容器行内尺寸的 1%)、cqb(容器块轴尺寸的 1%)、cqw(容器宽度)、cqh(容器高度)、cqmin(取 cqw 和 cqh 较小者)。这些单位允许组件的字体和间距相对于容器尺寸进行响应式调整。


延展阅读