为什么我们需要 Container Queries?
多年来,开发者一直依赖 CSS Media Queries 来实现响应式布局。但 Media Queries 有一个根本性的局限:它们只能基于视口(viewport)的尺寸来做判断。这意味着即使一个组件被放在一个很窄的侧边栏里,它也不知道自己的可用空间有多小——因为它只关心整个屏幕有多大。
这正是 Container Queries(容器查询)要解决的问题。CSS Container Queries 是 CSS 近年来最激动人心的特性之一,它允许组件根据其父容器的尺寸而非视口来调整自身的样式,真正实现了组件级别的响应式设计。
/* 传统方式:基于视口 */
@media (max-width: 768px) {
.card { flex-direction: column; }
}
/* 容器查询:基于父容器 */
.card-container {
container-type: inline-size;
container-name: card;
}
@container card (max-width: 400px) {
.card { flex-direction: column; }
}
核心概念与语法
1. 声明容器
要让一个元素成为查询容器,你需要使用 container-type 属性。它有两个主要值:
- inline-size:基于内联方向(通常是宽度)建立查询,最常用
- size:基于内联和块方向(宽度和高度)建立查询,但会同时启用两个轴的containment,性能开销更大
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
/* 简写形式 */
.sidebar {
container: sidebar / inline-size;
}
2. 编写容器查询规则
使用 @container 规则,语法与 @media 类似:
/* 基于容器名称的查询 */
@container sidebar (min-width: 400px) {
.widget {
display: grid;
grid-template-columns: 1fr 1fr;
}
}
/* 不包含容器名称的查询(匹配所有容器) */
@container (max-width: 300px) {
.card {
padding: 0.5rem;
}
}
/* 范围语法(现代浏览器支持) */
@container (400px <= width <= 800px) {
.card { font-size: 1rem; }
}
实战案例:自适应卡片组件
假设我们需要一个卡片组件,在不同容器宽度下呈现不同的布局:
<!-- 在任何地方都可以复用,自适应容器 -->
<div class="card-container">
<article class="card">
<div class="card__image">
<img src="photo.jpg" alt="封面" loading="lazy" />
</div>
<div class="card__body">
<h3 class="card__title">文章标题</h3>
<p class="card__desc">这是一段描述文字...</p>
<div class="card__meta">
<span>2024-01-15</span>
<span>· 5分钟阅读</span>
</div>
</div>
</article>
</div>
/* 声明容器 */
.card-container {
container-type: inline-size;
container-name: card;
}
/* 默认布局:紧凑模式(容器宽度 < 400px) */
.card {
display: flex;
flex-direction: column;
gap: 0.75rem;
padding: 0.75rem;
border: 1px solid #e2e8f0;
border-radius: 8px;
}
.card__image img {
width: 100%;
height: 180px;
object-fit: cover;
border-radius: 4px;
}
/* 中等宽度:水平布局(400px - 699px) */
@container card (min-width: 400px) {
.card {
flex-direction: row;
align-items: center;
}
.card__image {
flex: 0 0 160px;
}
.card__image img {
height: 120px;
}
.card__body {
flex: 1;
}
.card__title {
font-size: 1.1rem;
}
}
/* 宽容器:大图水平布局(>= 700px) */
@container card (min-width: 700px) {
.card {
flex-direction: row;
align-items: stretch;
gap: 1.25rem;
padding: 1.25rem;
}
.card__image {
flex: 0 0 320px;
}
.card__image img {
height: 220px;
}
.card__title {
font-size: 1.4rem;
margin-bottom: 0.5rem;
}
.card__desc {
font-size: 1rem;
line-height: 1.6;
}
}
案例二:CSS-only 响应式导航栏
不需要 JavaScript,仅用 CSS Container Queries 就能实现导航栏在窄容器中自动收起:
.nav-container {
container-type: inline-size;
container-name: nav;
}
/* 宽容器:水平展开导航 */
.navbar {
display: flex;
align-items: center;
gap: 1rem;
}
.nav-links {
display: flex;
gap: 1.5rem;
list-style: none;
margin-left: auto;
}
/* 窄容器:折叠菜单 */
@container nav (max-width: 500px) {
.navbar {
flex-wrap: wrap;
}
.nav-links {
display: none;
width: 100%;
flex-direction: column;
padding: 1rem 0;
gap: 0.75rem;
}
.nav-toggle {
display: block;
margin-left: auto;
}
.nav-toggle:checked ~ .nav-links {
display: flex;
}
}
Container Queries 与 Media Queries 对比
| 维度 | Media Queries | Container Queries |
|---|---|---|
| 查询基准 | 视口(viewport)宽度/高度 | 父容器(container)尺寸 |
| 复用性 | 依赖页面结构,组件不可复用 | 组件自适应,可在任意位置复用 |
| 代码语义 | 描述「屏幕大小」 | 描述「组件可用空间」 |
| 适用范围 | 整体页面布局 | 微观组件样式 |
| 浏览器支持 | ✅ 100%+ | ✅ Chrome 105+, Firefox 110+, Safari 16+ |
最佳实践:两者不是替代关系,而是互补。Media Queries 控制页面整体布局(如侧边栏宽度),Container Queries 控制组件内部样式(如网格列数、字号等)。
高级用法:容器查询单位
除了条件样式,Container Queries 还引入了新的长度单位,相对于容器尺寸计算:
- cqw:容器宽度的 1%
- cqh:容器高度的 1%
- cqi:容器内联方向尺寸的 1%
- cqb:容器块方向尺寸的 1%
- cqmin:cqi 和 cqb 中较小的值
- cqmax:cqi 和 cqb 中较大的值
.card-container {
container-type: inline-size;
}
.card-title {
/* 字号随容器宽度平滑变化 */
font-size: clamp(1rem, 5cqw, 2rem);
}
.card-icon {
/* 图标大小随容器同比缩放 */
width: 10cqw;
height: 10cqw;
}
这些单位特别适合图标、字体等需要随容器同比缩放的场景,避免了传统的媒体查询断点阶梯式跳变。
渐进增强方案
对于需要支持旧浏览器的项目,可以使用 @supports 做渐进增强:
/* 基础样式(所有浏览器) */
.card {
display: flex;
flex-direction: column;
}
/* 仅支持容器查询的浏览器生效 */
@supports (container-type: inline-size) {
.card-container {
container-type: inline-size;
container-name: card;
}
@container card (min-width: 400px) {
.card {
flex-direction: row;
}
}
}
再加上 @container 规则本身的特性——不支持的浏览器会直接忽略整个规则块,这比使用 JavaScript polyfill 更稳健。唯一的 polyfill(如 Google 的 container-query-polyfill)适用于需要完美兼容 IE11 的场景,但现代项目一般建议渐进增强即可。
总结
CSS Container Queries 代表了 CSS 向组件化方向的重要演进。它让组件开发者可以编写"一次定义、处处适应"的样式,不必再依赖外部环境来判断自己该显示成什么样子。
在现代浏览器支持率已经超过 90% 的 2026 年,是时候在你的项目中拥抱 Container Queries 了。从最基础的卡片组件开始,逐步用它替代那些"虽然能用但总觉得不够优雅"的媒体查询 hack,你会发现组件的复用性和可维护性都有了质的飞跃。

发表评论 取消回复