第十六章:CSS 容器查询(Container Queries)——响应式设计的革命性进化
传统的响应式设计依赖于视口尺寸(@media 查询),但当组件需要根据自身容器而非整个屏幕调整样式时,@media 就显得力不从心。
CSS 容器查询(Container Queries) 正是为解决这一痛点而生——它让组件真正实现了“内聚响应式”,是现代组件化开发的里程碑。
本章将带你掌握容器查询的核心语法,并通过实战案例构建一个智能响应式卡片布局。
1. 容器查询的引入背景:为什么需要它?
传统 @media 的局限
css
/* 问题:卡片样式取决于屏幕宽度,而非容器 */
@media (max-width: 600px) {
.card {
font-size: 14px;
padding: 12px;
}
}- ❌ 上下文丢失:卡片在小容器中仍可能过大
- ❌ 组件不独立:同一组件在不同布局中表现不一致
- ❌ 维护困难:需为每个使用场景编写特定媒体查询
容器查询的突破
✅ “组件知道自己的空间有多大”
卡片不再关心屏幕多宽,只关心父容器给它多少空间。
2. 如何根据父容器的尺寸调整样式?
关键步骤:
- 将元素设置为“查询容器”
- 使用
@container查询其尺寸 - 动态调整内部元素样式
3. 基本用法:@container 规则
① 定义查询容器
css
/* 指定该元素为“行内大小”(宽度)查询容器 */
.card-container {
container-type: inline-size;
container-name: card;
}
/* 简写 */
.card-container {
container: card / inline-size;
}container-type:inline-size: 行内尺寸(通常为宽度)block-size: 块级尺寸(通常为高度)size: 同时查询宽高
② 使用 @container 查询
css
@container card (min-width: 300px) {
.card-content {
display: flex;
}
.card-image {
width: 100px;
}
}
@container card (max-width: 299px) {
.card-content {
flex-direction: column;
}
.card-image {
width: 100%;
margin-bottom: 8px;
}
}③ 支持的查询条件
| 查询 | 说明 |
|---|---|
(min-width: 300px) | 容器宽度 ≥ 300px |
(max-height: 200px) | 容器高度 ≤ 200px |
(aspect-ratio: 1 / 1) | 容器宽高比 |
(style(--theme: light)) | 查询自定义样式属性(实验性) |
4. 浏览器支持与 Polyfill 方案
| 浏览器 | 支持情况 | 版本 |
|---|---|---|
| Chrome | ✅ 支持 | 105+ |
| Edge | ✅ 支持 | 105+ |
| Firefox | ✅ 支持 | 110+ |
| Safari | ✅ 支持 | 16.4+ |
| iOS Safari | ✅ 支持 | 16.4+ |
⚠️ 旧版浏览器降级方案
Polyfill 推荐:@container-query/polyfill
bash
npm install @container-query/polyfilljs
// main.js
import { applyContainerQueries } from '@container-query/polyfill';
applyContainerQueries();优雅降级(Graceful Degradation)
css
/* 默认窄版样式 */
.card-content {
display: block;
}
/* 仅在支持容器查询时覆盖 */
@supports (container-type: inline-size) {
@container card (min-width: 300px) {
.card-content {
display: flex;
}
}
}5. 实战案例:构建一个响应式卡片布局
需求
- 卡片在窄容器中:垂直堆叠(图片在上)
- 卡片在宽容器中:水平排列(图片在左)
- 支持网格布局中的不同列宽
HTML 结构
html
<div class="grid">
<div class="card-container">
<div class="card">
<img src="image.jpg" alt="Product" class="card-image">
<div class="card-content">
<h3 class="card-title">Product Name</h3>
<p class="card-desc">Short description here.</p>
</div>
</div>
</div>
<!-- 更多卡片... -->
</div>CSS 样式
css
.grid {
display: grid;
gap: 16px;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
}
/* 设置查询容器 */
.card-container {
container: card / inline-size;
}
.card {
border: 1px solid #ddd;
border-radius: 12px;
overflow: hidden;
background: white;
}
.card-image {
width: 100%;
aspect-ratio: 4 / 3;
object-fit: cover;
}
.card-content {
padding: 16px;
line-height: 1.5;
}
/* 宽容器:水平布局 */
@container card (min-width: 300px) {
.card {
display: flex;
}
.card-image {
width: 120px;
flex-shrink: 0;
border-radius: 0;
}
.card-content {
padding: 16px 16px 16px 0;
}
}
/* 窄容器:垂直布局 */
@container card (max-width: 299px) {
.card {
display: block;
}
.card-image {
width: 100%;
}
.card-content {
padding: 16px;
}
}效果
- 在 2列网格中(每列 ~300px+):卡片水平显示
- 在 3列或更窄网格中:卡片垂直堆叠
- 无需 JavaScript,纯 CSS 实现智能响应
结语:组件化时代的响应式新范式
容器查询标志着响应式设计从“页面驱动”迈向“组件驱动”。
它让组件真正实现了:
- 上下文感知:知道自己的可用空间
- 内聚响应:样式逻辑封装在组件内部
- 可复用性:同一组件在任何布局中都能正确显示
✅ 适用场景:
- 组件库(Card、List、Sidebar)
- 网格布局(Dashboard、Gallery)
- 可嵌入小部件(Widget、Ad)
容器查询不是 @media 的替代品,而是它的补充。
未来,我们将用 @media 控制页面结构,用 @container 控制组件内部。
下一章,我们将进入“视口单位与动态排版”。