如果你写过响应式页面,一定对媒体查询又爱又恨。它让布局能适配不同屏幕,但当同一个组件被放在页面主栏时是一套样式,被塞进侧边栏时却完全失控——媒体查询只能感知整个视口宽度,根本不知道自己的父容器多宽。容器查询的出现,就是来解决这个痛点的。目前主流浏览器都已支持,生产环境可以放心用起来。
一、从媒体查询到容器查询:一次思维转变
看一个典型的尴尬场景:你做了一个精美的文章卡片组件,在宽屏上展示成横排,窄屏上变成竖排,用的是 @media (max-width: 768px)。这个组件在首页主栏表现完美,但把它放到只有400px宽的侧边栏时,它仍然沿用宽屏的横排样式,元素挤成一团,因为视口宽度此时可能是1440px,媒体查询根本不会触发窄屏规则。
容器查询的逻辑是:让子元素能够根据直接父容器的尺寸变化来调整样式,而不是死盯着整个视口。这意味着同一个组件,无论放在哪里,都能根据自己所在的容器空间自动适配。这才是真正的组件级响应式。
二、核心语法与启用方式
容器查询的使用需要两步:先声明一个“容器”,再用 @container 规则写样式。
2.1 定义容器
给父元素设置 container-type 属性,告诉浏览器这个元素是尺寸可查询的容器:
.card-wrapper {
container-type: inline-size; /* 只监听内联方向(宽度)的变化 */
container-name: card-container; /* 可选,给容器起个名字 */
}
inline-size 是最常用的值,只关注宽度;如果还要观察高度变化,用 size。绝大部分布局问题只与宽度有关,用 inline-size 性能更好。
2.2 编写容器查询规则
用法和媒体查询几乎一样,只是把 @media 换成 @container:
@container card-container (max-width: 400px) {
.card {
flex-direction: column;
padding: 12px;
}
.card__image {
width: 100%;
margin-bottom: 8px;
}
}
如果只定义了一个容器,也可以省略容器名称,直接写 @container (max-width: 400px),它会匹配最近的上层容器。
三、实战案例一:自适应文章卡片
我们实现一个卡片组件,它有三种形态:容器宽度大于500px时展示完整横排大卡片,300px到500px之间展示紧凑横排,小于300px时变为纯竖排堆叠。整个组件只写一套HTML,放到不同宽度的父容器中自动变形。
HTML结构:
<div class="card-wrapper">
<article class="card">
<img class="card__cover" src="cover.jpg" alt="">
<div class="card__body">
<h3 class="card__title">深入理解CSS容器查询</h3>
<p class="card__desc">响应式新利器,让组件拥有自适应能力...</p>
<span class="card__date">2025-01-15</span>
</div>
</article>
</div>
基础样式先写好默认的横排布局,然后针对不同容器宽度做调整:
.card-wrapper {
container-type: inline-size;
container-name: card-container;
}
.card {
display: flex;
gap: 16px;
border-radius: 12px;
background: #fff;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}
.card__cover {
width: 200px;
object-fit: cover;
flex-shrink: 0;
}
.card__body {
padding: 16px;
display: flex;
flex-direction: column;
justify-content: center;
}
/* 容器宽度 300px ~ 500px:紧凑横排 */
@container card-container (min-width: 300px) and (max-width: 500px) {
.card__cover {
width: 120px;
}
.card__title {
font-size: 16px;
}
.card__desc {
font-size: 13px;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
}
/* 容器宽度小于300px:竖排堆叠 */
@container card-container (max-width: 299px) {
.card {
flex-direction: column;
}
.card__cover {
width: 100%;
height: 180px;
}
.card__body {
padding: 12px;
}
}
把这个卡片组件分别放在一个600px的主栏和一个280px的侧边栏里,它会自动切换样式,而一行媒体查询都不用改。组件的可移植性直接上了一个台阶。
四、实战案例二:动态网格导航栏
很多后台界面顶部有一个导航栏,里面包含Logo、菜单项、搜索框和用户头像。当空间变窄时,我们不想让元素换行,而是希望部分次要菜单自动收进一个“更多”下拉里。用容器查询可以精确控制这个临界点。
关键思路:利用容器查询检测宽度,隐藏某些菜单项,同时显示一个“更多”按钮。这里不再需要JavaScript监听宽度变化,CSS直接完成。
.navbar-wrapper {
container-type: inline-size;
container-name: navbar;
}
.nav {
display: flex;
align-items: center;
gap: 16px;
list-style: none;
padding: 0 24px;
}
.nav__item--secondary {
display: inline-flex;
}
.nav__more {
display: none;
}
/* 当导航容器小于700px时,隐藏次级菜单,显示“更多”按钮 */
@container navbar (max-width: 700px) {
.nav__item--secondary {
display: none;
}
.nav__more {
display: inline-flex;
}
}
这个方案比用JS监听 resize 再操作DOM要轻量且无抖动,尤其适合在微前端或者多框架混用的场景里保持样式稳定。
五、实战案例三:带网格的仪表盘小部件
仪表盘通常由多个可拖拽的小部件组成,每个部件内部用CSS Grid排列一些数据项。当用户调整某个部件的大小时,内部网格应该自动调整列数。容器查询配合Grid可以很自然地实现。
.widget {
container-type: inline-size;
container-name: widget;
}
.stats-grid {
display: grid;
gap: 12px;
grid-template-columns: repeat(4, 1fr);
}
/* 3列 */
@container widget (max-width: 600px) {
.stats-grid {
grid-template-columns: repeat(3, 1fr);
}
}
/* 2列 */
@container widget (max-width: 400px) {
.stats-grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* 单列 */
@container widget (max-width: 250px) {
.stats-grid {
grid-template-columns: 1fr;
}
}
这样无论小部件被放在多宽的容器里,或者用户动态缩放浏览器窗口导致容器宽度改变,内部统计卡片始终以合适的列数呈现,阅读体验不会崩。
六、几个踩坑点与最佳实践
- 容器本身的高度:容器查询默认只监听宽度,如果想让容器根据自身高度变化调整内部样式,需要
container-type: size,但此时容器本身的高度如果没有显式定义,可能会塌陷。通常需要给容器一个确定的height或者让内容撑开高度后再利用@container (min-height: ...)查询,实际场景中用得较少。 - 查询单位:容器查询引入了新的单位
cqw(容器宽度的1%)和cqh(容器高度的1%),可以在子元素里直接用,比如font-size: clamp(14px, 4cqw, 24px);,让字号随容器宽度平滑缩放,非常适合卡片标题。 - 性能:容器查询本身性能损耗很小,但过度嵌套容器(比如表格每个单元格都声明
container-type)会增加布局计算成本,建议只在真正需要组件级响应的包装层使用。 - 兼容性:Chrome 105+、Safari 16+、Firefox 110+ 均已支持。如果你的用户群体仍包含旧版浏览器,可以用
@supports (container-type: inline-size)做特性检测,降级方案回退到固定样式或媒体查询。
七、总结
容器查询把响应式控制权从视口交还给了父容器,这让组件的封装性和复用性有了质的提升。结合 :has() 选择器、CSS Nesting 等新特性,现代CSS已经具备了从前需要预处理器和大量JS才能实现的表达能力。上面三个案例覆盖了卡片、导航、网格三种最常见的场景,可以直接复用到实际项目中,你会发现调整布局的工作量瞬间减少一半。

