← 返回博客列表

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 的脚本和一次布局抖动。

如果你正在重构一个组件库,容器查询值得作为第一优先级考虑的能力之一。