长久以来,响应式设计几乎完全依赖媒体查询(Media Queries)——根据视口宽度调整页面布局。然而,当开发一个组件库时,我们常常希望同一个组件能够根据其父容器的可用空间自适应,而不是整个视口。例如,在侧边栏中显示的卡片应该呈现紧凑布局,而在主内容区域内则应展开显示更多信息。CSS 容器查询(Container Queries)正是为此而生,它允许开发者根据容器的尺寸定义样式,真正实现了“组件级别的响应式”。本文将带你彻底掌握这一革命性特性,并通过一个完整的卡片组件案例,演示如何用 @container 规则摆脱媒体查询的束缚。
一、容器查询的核心概念
要使用容器查询,首先需要定义一个包含上下文(Containment Context)。这是通过 container-type 属性完成的。它告诉浏览器:该元素是一个尺寸容器,其子元素可以查询它的尺寸。常用的值有:
container-type: inline-size— 建立内联轴(通常为宽度)查询容器。container-type: size— 同时建立内联轴和块轴的查询容器(较少使用)。container-type: normal— 默认值,不建立容器。
此外,还可以通过 container-name 为容器命名,以便在多个嵌套容器中精确指定查询目标。
.sidebar {
container-type: inline-size;
container-name: sidebar-container; /* 可选命名 */
}
一旦容器被定义,其内部的元素就可以使用 @container 规则来根据该容器的尺寸改变样式:
@container (min-width: 400px) {
.card {
display: flex;
flex-direction: row;
}
}
这会在容器宽度至少为400像素时,将 .card 的布局改为水平排列。注意,这里查询的是容器的宽度,而非视口宽度。这就是容器查询的核心威力。
二、为容器指定查询上下文
要建立一个容器,必须显式设置 container-type(或者简写属性 container)。同时,为了确保容器能正确工作,还需要了解 containment 的概念——容器查询会自动为该元素应用 layout、style 和 paint 的包含(containment),这意味着容器的尺寸不会因为子元素的变化而无限波动。
简写属性 container 可以同时设置名称和类型:
.widget-area {
container: widget-box / inline-size;
}
如果不需要名称,可以直接使用 container-type: inline-size。下面是一个基础的容器定义示例:
<div class="container">
<div class="child">...</div>
</div>
.container {
container-type: inline-size;
}
@container (min-width: 500px) {
.child {
background-color: lightblue;
}
}
当 .container 的宽度超过500px时,子元素 .child 的背景色就会改变。这完全独立于视口尺寸。
三、实战案例:构建一个完全自适应的卡片组件
我们来创建一个可复用的卡片组件(.post-card),它无论被放置在宽窄不同的容器中,都能自动切换最适合的布局。这个卡片包含一张图片、标题、一段描述和一个按钮。我们要求:
- 当容器宽度小于300px时,采用垂直堆叠布局。
- 当容器宽度在300px到600px之间时,采用水平布局,图片在左,文字在右。
- 当容器宽度大于600px时,图片放大,文字和图片各占一半,并且描述字体增大。
步骤1:HTML结构
<section class="page">
<div class="narrow-area">
<article class="post-card">
<img src="image.jpg" alt="" class="post-card-image">
<div class="post-card-body">
<h2 class="post-card-title">容器查询入门</h2>
<p class="post-card-desc">这是描述文本,可根据容器自动调整。</p>
<button class="post-card-btn">阅读更多</button>
</div>
</article>
</div>
<div class="wide-area">
<article class="post-card">
<!-- 完全相同的卡片结构 -->
</article>
</div>
</section>
步骤2:定义容器
.narrow-area {
container-type: inline-size;
container-name: narrow-box;
width: 280px; /* 模拟窄容器 */
}
.wide-area {
container-type: inline-size;
container-name: wide-box;
width: 750px; /* 模拟宽容器 */
}
注意,我们为每个区域定义了独立的容器,并且使用了不同的名称。不过卡片查询时可以直接查询最近的祖先容器,名称是可选的。为了更清晰的演示,我们只使用 container-type 而不强制名称,让卡片查询最近的祖先容器。
步骤3:使用@container规则调整卡片样式
/* 基础样式(最窄布局) */
.post-card {
border: 1px solid #ddd;
border-radius: 8px;
overflow: hidden;
display: flex;
flex-direction: column;
}
.post-card-image {
width: 100%;
height: auto;
display: block;
}
.post-card-body {
padding: 1rem;
}
.post-card-title {
font-size: 1.1rem;
margin: 0 0 0.5rem;
}
.post-card-desc {
font-size: 0.85rem;
color: #666;
margin: 0 0 1rem;
}
.post-card-btn {
padding: 0.5rem 1rem;
border: none;
background: #0066cc;
color: white;
border-radius: 4px;
cursor: pointer;
}
/* 容器宽度大于等于400px时的布局 */
@container (min-width: 400px) {
.post-card {
flex-direction: row;
}
.post-card-image {
width: 40%;
max-width: 200px;
object-fit: cover;
}
.post-card-body {
flex: 1;
}
}
/* 容器宽度大于等于650px时进一步优化 */
@container (min-width: 650px) {
.post-card {
align-items: center;
}
.post-card-image {
width: 45%;
max-width: 300px;
}
.post-card-title {
font-size: 1.5rem;
}
.post-card-desc {
font-size: 1rem;
}
.post-card-btn {
padding: 0.6rem 1.5rem;
font-size: 1rem;
}
}
通过以上样式,同一套HTML代码在窄容器(280px)中表现为垂直卡片,在400px至650px之间的容器中表现为左图右文,在更宽的容器中则呈现更舒展的横向布局。这一切不依赖任何媒体查询或JavaScript,完全由容器尺寸驱动。
四、容器查询长度单位
CSS还引入了一组新的相对单位,专门用于容器查询上下文,非常有用:
cqw:容器宽度的1%cqh:容器高度的1%cqi:容器内联轴尺寸的1%cqb:容器块轴尺寸的1%cqmin、cqmax:容器尺寸较小/较大值的1%
这些单位可以让元素的大小或间距直接与容器尺寸挂钩,而不需要计算。例如,我们想在卡片内部设置一个标题字体大小为容器宽度的5%,可以这样做:
.post-card-title {
font-size: 5cqi; /* 基于容器内联尺寸 */
}
利用这些单位可以创建完全流体的组件内部尺寸,进一步减少对断点的依赖。
五、嵌套容器与命名容器的精细控制
在复杂的组件树中,可能存在多层容器。如果希望内部的某个元素只查询特定的祖先容器,而不是最近的那个,就可以使用 container-name 配合 @container 语法:
.outer {
container-type: inline-size;
container-name: outer;
}
.inner-container {
container-type: inline-size;
container-name: inner;
}
/* 只查询名为 outer 的容器 */
@container outer (min-width: 500px) {
.target {
background: coral;
}
}
这种机制让样式控制变得更加精确,尤其适合在微前端或复杂布局中管理不同层级的响应。
六、浏览器兼容性与渐进增强策略
截至2025年,容器查询在所有现代浏览器(Chrome 105+、Edge 105+、Safari 16+、Firefox 110+)中均已得到良好支持,全球覆盖率超过93%,可以直接用于生产环境。对于需要兼容旧版浏览器的项目,可以采用渐进增强策略:
- 先提供一套不依赖容器查询的基础样式(例如垂直布局)。
- 将容器查询的样式放在
@supports (container-type: inline-size)中,或者直接编写,因为不支持的浏览器会忽略整个@container规则块。 - 如果需要更精确的polyfill,可以使用
cq-prolyfill等库。
/* 基础降级样式 */
.card {
display: block;
}
/* 容器查询增强 */
@container (min-width: 400px) {
.card {
display: flex;
}
}
不支持容器查询的浏览器只会看到基础块布局,而现代浏览器会自动应用增强的弹性布局,实现完美的渐进增强。
七、总结
CSS容器查询彻底改变了响应式设计的方式,将控制权从全局视口归还给了每个组件所在的局部容器。本文从建立容器上下文开始,逐步深入到 @container 规则、容器查询单位、命名容器和嵌套场景,并通过一个完整的自适应卡片案例演示了其强大潜力。现在,你可以自信地在组件库中采用容器查询,让每个组件都能在任何环境中优雅地呈现。
拥抱容器查询,意味着真正实现“一次构建,处处自适应”——这正是现代CSS给予开发者最有力的工具之一。

