CSS 容器查询实战笔记
容器查询让组件真正拥有了自适应能力。本文通过几个真实场景总结用法、边界与降级方案。
媒体查询只回答一个问题:视口有多宽? 但组件真正需要知道的,是它自己的容器有多宽。同样一个卡片组件,放进侧边栏是 260px,放进主内容区是 720px——用媒体查询去适配这种场景,要么写死一套断点,要么每个容器配一套类名,都不优雅。
容器查询(Container Queries)解决的就是这个问题:让组件根据父容器的尺寸来调整自身样式。
基本用法
容器查询由两部分组成:在父元素上声明「这是一个容器」,在子元素上写查询规则。
.card-list {
container-type: inline-size; /* 把 .card-list 声明为容器 */
}
.card {
/* 当容器宽度 >= 480px 时生效 */
@container (min-width: 480px) {
.card-title {
font-size: 1.25rem;
}
}
}
container-type 有三种取值:
inline-size:只跟踪内联轴(宽度),最常用,性能最好。size:同时跟踪宽高,但要求容器自身不能依赖内容尺寸(容易引起循环依赖,慎用)。normal:声明为容器但不建立尺寸约束,通常配合container-name使用。
实战场景:自适应卡片
想象一个商品卡片,在窄容器里信息竖排,在宽容器里图片和文字并排。以前要写两套结构或依赖媒体查询,现在可以这样:
.product {
container-type: inline-size;
}
.product__body {
display: grid;
gap: 0.75rem;
}
/* 容器宽了,改成左右布局 */
@container (min-width: 520px) {
.product__body {
grid-template-columns: 160px 1fr;
align-items: center;
}
.product__price {
font-size: 1.5rem;
}
}
这种写法的好处是组件级自治:把 .product 拖到任何地方,它的表现都会自动匹配容器宽度,不需要外部配合。
命名容器与容器单位
当一个页面里有多个容器时,查询可能变得模糊。container-name 可以精确指定查询哪个容器:
.sidebar {
container-name: sidebar;
container-type: inline-size;
}
/* 只有 sidebar 容器满足条件时才生效 */
@container sidebar (min-width: 300px) {
.nav-link {
display: block;
}
}
容器查询还带来了一组新单位,以 cq 开头:
| 单位 | 含义 |
|---|---|
cqw |
容器宽度的 1% |
cqh |
容器高度的 1% |
cqi |
容器内联轴尺寸的 1% |
cqmin / cqmax |
上述两者中较小 / 较大者 |
利用 cqi 可以让字号、间距随容器平滑缩放:
.card {
font-size: clamp(0.9rem, 2.5cqi, 1.4rem);
padding: clamp(0.75rem, 3cqi, 1.5rem);
}
注意容器单位在容器本身(即被查询的元素)上无效——这一点和视口单位的差异类似,容易踩坑。
边界与踩坑
实战中我遇到的坑,按频率排序:
1. 容器高度塌陷。 默认情况下 container-type: inline-size 会让容器在块轴变成 contain,即不把内容高度算进布局,常见表现是容器高度为 0。解决办法:给容器一个显式高度,或者用 container-type: inline-size + 普通内容模式——大多数情况下你应该配合 min-height 或网格布局使用。
2. 样式隔离导致的继承问题。 容器会建立新的布局和样式包含上下文(类似 contain 的行为),部分外部动画、position: fixed 定位的祖先会影响。若遇到诡异表现,优先怀疑是不是容器切断了继承。
3. 查询条件不能引用兄弟容器。 @container 只能查询祖先容器,不能写「当前元素左边那个兄弟多宽」。这是设计上的限制,不是 bug。
降级方案
容器查询的浏览器支持目前已经相当好(Chrome 105+、Safari 16+、Firefox 110+),但如果需要兼容更老的浏览器,常用的降级策略是:
/* 先写媒体查询兜底 */
@media (min-width: 520px) {
.product__body {
grid-template-columns: 160px 1fr;
}
}
/* 再写容器查询增强 */
@container (min-width: 520px) {
.product__body {
grid-template-columns: 160px 1fr;
}
}
CSS 的层叠规则保证了两者同时存在时以容器查询为准,而老浏览器不认识 @container 时会自动忽略,回退到媒体查询逻辑。唯一要注意的是选择器写法的一致性——两段规则要真的产生同样的效果,否则会出现「新版用新布局、旧版用另一套布局」的分裂。
小结
容器查询补上了响应式设计里「按容器而非视口适配」这块拼图。它让组件真正做到开箱即用、随处可用。结合 CSS 自定义属性,很多过去要靠 JavaScript 监听 ResizeObserver 才能实现的效果,现在纯 CSS 就能完成,还少了那 2KB 的脚本和一次布局抖动。
如果你正在重构一个组件库,容器查询值得作为第一优先级考虑的能力之一。