CSS Container Queries:终于不用再依赖视口宽度做响应式了
深入理解 CSS Container Queries 的语法与实战,让你的组件真正做到「放哪都好看」,而不只是「屏幕变窄就堆叠」。
之前做一个后台管理系统,有个卡片组件同时用在侧边栏(240px 宽)和主内容区(800px 宽)。用 Media Query 根据视口宽度来切换布局完全不靠谱——因为视口可能是 1440px,但侧边栏里的卡片依然只有 240px 的空间。我只能用 ResizeObserver + JavaScript 来监听容器宽度动态加 class,写得又丑又脆弱。
Container Queries 是 CSS 社区呼唤了十多年的功能。从 2015 年就有提案,一直因为「循环依赖」的技术难题卡着。直到 2023 年才在主流浏览器全面支持。十年啊,这期间我们写了多少 ResizeObserver hack、多少 JS-based responsive 方案。CSS 标准的推进速度有时候真的让人窒息。
1. Media Query 的局限性
Media Query 只能查询视口(viewport)的尺寸,无法感知组件所在容器的大小。这在组件化开发时代是个根本性的问题:
/* 这段代码的意思是:当 *浏览器窗口* 小于 768px 时堆叠 */
/* 但如果组件在侧边栏里,窗口是 1440px,组件依然很窄 */
@media (max-width: 768px) {
.card {
flex-direction: column;
}
}
/* 我们真正想要的是:当 *组件所在容器* 小于 400px 时堆叠 */
/* 这就是 Container Queries 做的事 */同一个卡片组件,放在三栏布局的窄列里应该是垂直排列,放在宽列里应该是水平排列。这个需求用 Media Query 无法优雅实现,因为视口宽度和组件实际可用空间没有直接关系。
2. Container Queries 基础语法
使用 Container Queries 分两步:先声明容器(containment context),再编写容器查询规则。
第一步:声明容器
/* 方式一:用 container-type 声明 */
.sidebar {
container-type: inline-size; /* 监听内联方向(通常是宽度)的大小 */
}
/* 方式二:同时给容器命名(推荐) */
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
/* 方式三:简写 */
.sidebar {
container: sidebar / inline-size;
}第二步:编写 @container 规则
/* 当最近的容器祖先宽度大于 400px 时 */
@container (min-width: 400px) {
.card {
flex-direction: row;
gap: 1.5rem;
}
}
/* 查询指定名称的容器 */
@container sidebar (max-width: 300px) {
.card {
font-size: 0.875rem;
}
}container-type 的可选值:
| 值 | 含义 |
|---|---|
| inline-size | 只监听内联方向(宽度)变化,最常用 |
| size | 同时监听宽度和高度变化 |
| normal | 默认值,不建立容器上下文 |
3. 实战:自适应卡片组件
来看一个最经典的场景:一个卡片组件需要在不同宽度的容器中自动切换布局。
HTML 结构
<div class="card-container">
<article class="card">
<img class="card-image" src="cover.jpg" alt="封面" />
<div class="card-body">
<h3 class="card-title">文章标题</h3>
<p class="card-desc">这是一段描述文字...</p>
<span class="card-meta">2026-03-25</span>
</div>
</article>
</div>CSS(Container Queries 版本)
/* 声明容器 */
.card-container {
container: card / inline-size;
}
/* 默认:窄容器,垂直布局 */
.card {
display: flex;
flex-direction: column;
}
.card-image {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
border-radius: 0.5rem 0.5rem 0 0;
}
/* 容器 >= 400px:水平布局 */
@container card (min-width: 400px) {
.card {
flex-direction: row;
gap: 1rem;
}
.card-image {
width: 200px;
aspect-ratio: 1;
border-radius: 0.5rem 0 0 0.5rem;
}
}
/* 容器 >= 700px:更宽松的布局 */
@container card (min-width: 700px) {
.card-image {
width: 300px;
}
.card-title {
font-size: 1.5rem;
}
}4. 容器查询单位(Container Query Units)
除了 @container 条件查询,CSS 还引入了一组全新的长度单位,它们相对于容器的尺寸而不是视口:
| 单位 | 含义 | 对标视口单位 |
|---|---|---|
| cqw | 容器宽度的 1% | vw |
| cqh | 容器高度的 1% | vh |
| cqi | 容器内联尺寸的 1% | vi |
| cqb | 容器块尺寸的 1% | vb |
| cqmin | cqi 和 cqb 中较小的那个 | vmin |
| cqmax | cqi 和 cqb 中较大的那个 | vmax |
实用示例:响应式字体大小
/* 标题字号随容器宽度缩放 */
.card-title {
/* 最小 1rem,最大 2rem,中间按容器宽度的 5% 缩放 */
font-size: clamp(1rem, 5cqi, 2rem);
}
/* 内边距也跟着容器走 */
.card-body {
padding: 3cqi;
}5. Style Queries(实验性)
Container Queries 不只能查询尺寸,还有一个实验性特性:Style Queries,可以查询容器的计算样式值(目前仅限 CSS 自定义属性)。
/* 根据容器的 CSS 变量值来决定样式 */
.theme-container {
--theme: dark;
}
@container style(--theme: dark) {
.card {
background: #1a1a2e;
color: #eee;
}
}
@container style(--theme: light) {
.card {
background: #fff;
color: #333;
}
}注意:Style Queries 目前仅 Chrome 111+ 支持,且只能查询自定义属性(CSS Variables)。查询计算属性(如 style(color: red))尚未在任何浏览器实现。生产环境请谨慎使用。
6. 浏览器兼容性与渐进增强
Size Container Queries(@container 尺寸查询)已在所有主流浏览器获得支持:Chrome 105+、Firefox 110+、Safari 16+。全球支持率已超过 92%。
渐进增强策略
/* 基础样式:所有浏览器都能用 */
.card {
display: flex;
flex-direction: column;
}
/* 支持 container queries 的浏览器享受更好的体验 */
@supports (container-type: inline-size) {
.card-container {
container-type: inline-size;
}
@container (min-width: 400px) {
.card {
flex-direction: row;
}
}
}
/* 不支持的浏览器回退到 media query */
@supports not (container-type: inline-size) {
@media (min-width: 768px) {
.card {
flex-direction: row;
}
}
}实践建议:2026 年了,除非你需要支持非常老旧的浏览器,否则可以放心在生产环境使用 Container Queries。对于不支持的浏览器,用 Media Query 作为回退即可——用户不会得到「最优」的响应式体验,但也不会看到坏掉的布局。
7. 在 Tailwind CSS 中使用
如果你用 Tailwind CSS,Container Queries 同样开箱可用。Tailwind v3.2+ 通过官方插件支持,v4 已经内置。
<!-- 声明容器 -->
<div class="@container">
<!-- 容器查询 -->
<div class="flex flex-col @md:flex-row @lg:gap-6">
<img class="w-full @md:w-48 @lg:w-64" />
<div class="p-4">
<h3 class="text-base @lg:text-xl">标题</h3>
</div>
</div>
</div>
<!-- 命名容器 -->
<div class="@container/sidebar">
<div class="@sm/sidebar:flex-row">...</div>
</div>相关阅读
想了解更多现代 CSS 布局技巧?推荐阅读 CSS 现代布局完全指南:Flexbox 和 Grid 从入门到实战,以及 CSS 动画性能优化。