目录

CSS Container Queries 实战:告别媒体查询,实现真正组件级的响应式设计

为什么需要 Container Queries?

传统响应式设计依赖 @media 查询,它只能根据视口(viewport)尺寸调整样式。但现代 UI 中,一个组件可能在侧边栏、弹窗、全屏页等不同宽度的容器里复用——媒体查询完全无能为力。

1
2
3
4
/* 传统方式:依赖视口宽度 */
@media (max-width: 600px) {
  .card { flex-direction: column; }
}

上面这段代码的问题在于:即使 .card 在 400px 宽的侧边栏里,它也会根据 600px 以下的视口规则变成纵向排列,这太死板了。

CSS Container Queries 解决了这个痛点——它让组件根据自身父容器的尺寸响应,真正做到组件级自适应。

基础语法三步走

第一步:定义容器

给父元素设置 container-type,告诉浏览器它是一个查询容器:

1
2
3
4
5
/* 最简单的容器定义 */
.card-container {
  container-type: inline-size;  /* 仅监听宽度变化 */
  /* container-type: size; */   /* 同时监听宽高(较少用) */
}

inline-size 是最常用的值,它只监听容器的内联方向(对中文/英文就是宽度),性能更好。

你也可以给容器命名,以便在多个容器中精确定位:

1
2
3
4
5
6
7
8
9
.sidebar {
  container-type: inline-size;
  container-name: sidebar;
}

/* 简写 */
.main-area {
  container: main / inline-size;
}

第二步:编写容器查询

定义好容器后,在子孙元素中使用 @container

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@container (min-width: 500px) {
  .card {
    display: grid;
    grid-template-columns: 200px 1fr;
  }
}

/* 命名容器的查询 */
@container sidebar (max-width: 300px) {
  .card-title {
    font-size: 0.9rem;
  }
}

第三步:完整的实战示例

一个在产品列表、推荐侧栏、购物车弹窗中都能自适应布局的商品卡片:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
<div class="product-shelf">
  <div class="product-card">
    <img src="product.jpg" alt="商品图" class="product-image" />
    <div class="product-info">
      <h3 class="product-name">无线降噪耳机</h3>
      <p class="product-desc">Hi-Res 音质,40小时续航</p>
      <span class="product-price">¥499</span>
      <button class="buy-btn">加入购物车</button>
    </div>
  </div>
</div>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
.product-shelf {
  container-type: inline-size;
}

.product-card {
  display: flex;
  gap: 1rem;
  padding: 1rem;
  border-radius: 8px;
  background: #fff;
}

.product-image {
  width: 120px;
  height: 120px;
  object-fit: cover;
  border-radius: 8px;
}

/* 容器窄于 400px:上方图片,下方文字(纵向堆叠) */
@container (max-width: 400px) {
  .product-card {
    flex-direction: column;
  }
  .product-image {
    width: 100%;
    height: 200px;
  }
  .product-name {
    font-size: 1rem;
  }
  .product-desc {
    display: none; /* 空间不够,隐藏详情 */
  }
}

/* 容器中等宽度:保留描述,调整字号 */
@container (min-width: 401px) and (max-width: 700px) {
  .product-price {
    font-size: 1.1rem;
    color: #e74c3c;
  }
  .buy-btn {
    padding: 0.4rem 0.8rem;
    font-size: 0.85rem;
  }
}

容器查询长度单位

Container Queries 还引入了 cq 单位,作用类似 vw/vh,但参照的是容器尺寸而非视口:

单位 参照
cqw 容器宽度的 1%
cqh 容器高度的 1%
cqi 容器内联方向尺寸的 1%(推荐)
cqb 容器块方向尺寸的 1%
cqmin cqi 和 cqb 中较小的值
cqmax cqi 和 cqb 中较大的值

实战例子:让字体随容器动态缩放

1
2
3
.card-title {
  font-size: clamp(0.875rem, 3cqi, 1.5rem);
}

这段代码让标题字号在容器宽度的 3% 和上下限之间自适应——容器越宽字越大,缩窄时自动减小,完美响应式字体。

实战:响应式数据表格

表格在不同宽度容器中呈现截然不同的形态,是 Container Queries 的最佳场景:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
.data-table-wrapper {
  container-type: inline-size;
}

/* 宽容器:标准表格 */
@container (min-width: 600px) {
  .data-table {
    display: table;
    width: 100%;
    border-collapse: collapse;
  }
  .data-table th,
  .data-table td {
    padding: 0.75rem;
    border-bottom: 1px solid #eee;
  }
}

/* 中等容器:卡片式表格 */
@container (min-width: 400px) and (max-width: 599px) {
  .data-table {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.5rem;
  }
  .data-table tr {
    display: contents;
  }
  .data-table th { display: none; }
  .data-table td {
    padding: 0.5rem;
    background: #f8f9fa;
    border-radius: 4px;
  }
}

/* 窄容器:列表式 */
@container (max-width: 399px) {
  .data-table, .data-table tbody {
    display: block;
  }
  .data-table tr {
    display: block;
    margin-bottom: 1rem;
    padding: 0.75rem;
    background: #fff;
    border: 1px solid #eee;
    border-radius: 8px;
  }
  .data-table th { display: none; }
  .data-table td {
    display: flex;
    justify-content: space-between;
    padding: 0.25rem 0;
  }
  /* 用 data-label 属性模拟表头 */
  .data-table td::before {
    content: attr(data-label);
    font-weight: 600;
    color: #666;
  }
}

对应的 HTML:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<div class="data-table-wrapper">
  <table class="data-table">
    <thead>
      <tr>
        <th>姓名</th><th>部门</th><th>职位</th><th>入职时间</th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td data-label="姓名">张三</td>
        <td data-label="部门">研发部</td>
        <td data-label="职位">高级工程师</td>
        <td data-label="入职时间">2024-03-15</td>
      </tr>
      <!-- 更多行... -->
    </tbody>
  </table>
</div>

注意事项与最佳实践

  1. 不要对每个元素都设容器:容器定义有性能开销,只在真正需要组件级响应的父级设置。

  2. 容器不能查询自身@container 查的是最近的祖先容器,不能查自身。需要自引用时用 ResizeObserver 配合 JS。

  3. 容器链只认一个:嵌套容器时,@container 默认向上找到最近的容器。想精确控制用 container-name

  4. 兼容性:2026 年所有主流浏览器早已完全支持 Container Queries(Chrome 105+, Safari 16+, Firefox 110+),生产环境可以放心使用。

  5. 配合 Grid/Flexbox 更强大:Container Queries + CSS Grid + clamp() 三件套,可以写出不需要任何 JS 就能完美自适应的复杂组件。

小结

Container Queries 是 CSS 这几年最具革命性的特性之一。它把"响应式"的责任从页面级下放到组件级,让真正的组件化开发成为可能。下次再遇到「这个组件在侧边栏和主页中布局不同」的需求时,别再写两套组件逻辑了——交给 @container 吧。