CSS Container Queries 容器查询
CSS Container Queries 完全指南
长期以来,响应式设计主要依赖 Media Queries(媒体查询),根据视口(viewport)的宽度调整样式。但组件往往需要根据自身父容器的可用空间,而非整个视口来决定布局。Container Queries(容器查询) 解决了这一痛点,让你能够创建真正独立、可复用的响应式组件。
1. 什么是容器查询?
容器查询允许你基于一个容器元素的尺寸(宽度、高度、内联尺寸等),而非浏览器视口,来应用 CSS 样式。这意味着组件可以根据放置它的空间大小自适应,无论该空间是整个页面、一个侧边栏还是一个网格单元格。
核心概念对比:
- 媒体查询:
@media (min-width: 768px)→ 询问视口宽度是否 ≥768px - 容器查询:
@container (min-width: 400px)→ 询问当前包含块(定义为容器的祖先)的宽度是否 ≥400px
2. 容器查询的基本语法
使用容器查询分为两步:定义容器和编写查询规则。
2.1 定义容器
默认情况下,任何元素都不是查询容器。你需要使用 container-type 属性显式声明一个元素为容器。也可以使用 container 简写同时设置类型和名称。
/* 将元素定义为内联尺寸容器(查询宽度) */
.container {
container-type: inline-size;
}
/* 同时设置容器名称(便于多容器区分) */
.card-wrapper {
container: card-container / inline-size;
}
container-type 可选值:
inline-size:创建一个查询元素内联尺寸(通常是宽度,取决于书写模式)的容器。适合大多数横向布局。size:同时查询元素的内联和块级尺寸(宽度和高度)。某些布局可能需要高度查询。normal:默认值,元素不是查询容器。
定义容器后,它便成为一个“包含上下文”(Containment Context),这可能会影响内部绝对定位元素的行为。
2.2 编写容器查询规则
使用 @container 规则,语法与媒体查询极其相似:
@container (min-width: 500px) {
.card {
display: grid;
grid-template-columns: 1fr 2fr;
}
}
你可以添加容器名称来限定查询范围:
@container card-container (min-width: 400px) {
.card {
flex-direction: row;
}
}
逻辑运算符同样支持:and、or、not。也支持范围语法 (width > 300px)。
3. 容器查询长度单位
为配合容器查询,CSS 引入了与容器尺寸相对的单位:
cqw:容器宽度的 1%cqh:容器高度的 1%cqi:容器内联尺寸的 1%cqb:容器块级尺寸的 1%cqmin:cqi与cqb中较小值的 1%cqmax:cqi与cqb中较大值的 1%
这些单位让字体、间距、边框半径等随容器缩放变得异常简单:
.card__title {
font-size: 3cqi; /* 标题字体为容器内联尺寸的 3% */
padding: 2cqi;
}
4. 第一个容器查询示例
下面创建一个简单的卡片组件,它会在容器宽度较小时纵向堆叠,容器变宽时横向排列。
HTML 结构:
<div class="card-container">
<div class="card">
<img src="image.jpg" alt="" class="card__image">
<div class="card__content">
<h2>标题</h2>
<p>这是卡片的描述内容,会根据容器宽度重新布局。</p>
</div>
</div>
</div>
CSS 样式:
/* 定义容器 */
.card-container {
container-type: inline-size;
}
/* 基础样式:纵向布局 */
.card {
display: flex;
flex-direction: column;
gap: 1rem;
padding: 1rem;
border: 1px solid #ccc;
}
/* 容器宽度大于 450px 时,变为横向 */
@container (min-width: 450px) {
.card {
flex-direction: row;
}
.card__image {
width: 40%;
object-fit: cover;
}
.card__content {
flex: 1;
}
}
将 .card-container 放入一个窄栏或宽栏,卡片会自动适应,无需依赖视口宽度。
5. 容器查询与媒体查询的协作
两者并非替代关系,而是互补:
- 媒体查询:用于页面级布局,如栅格系统、导航栏形式改变。
- 容器查询:用于组件级响应,如卡片内部重排、组件内元素的显隐。
一个典型的组合用法:页面使用 Media Query 调整网格列数,组件内部使用 Container Query 根据网格单元格实际大小微调细节。
6. 命名容器与多层嵌套
当页面包含多个不同容器时,通过容器名称精准控制。
.sidebar {
container: sidebar-container / inline-size;
}
.main-content {
container: main-container / inline-size;
}
/* 仅对 sidebar 容器内的元素应用 */
@container sidebar-container (max-width: 300px) {
.nav-item {
display: block;
}
}
如果嵌套定义容器,内部的元素会查询最近的祖先容器,除非你指定了容器名称。未命名的查询(@container 不带名称)会匹配最近的非命名容器。
7. 使用容器查询长度单位实现动态排版
结合 cqi 单位,可以实现完全流畅的组件:
.card {
container-type: inline-size;
/* 其他样式 */
}
.card__title {
font-size: clamp(1rem, 5cqi, 2.5rem);
}
这样标题大小会随着容器宽度平滑缩放,不会在任何断点处突变,始终保持可读性。
8. 条件查询:style() 查询(实验性)
容器查询规范还扩展了 样式查询(Style Queries),允许你根据容器自身的自定义属性值(CSS 变量)来决定子元素的样式。目前大多数浏览器已支持。
@container style(--theme: dark) {
.child {
color: #fff;
background: #333;
}
}
这为基于“主题”或“模式”的样式提供了原生 CSS 方案,无需额外类名。
9. 浏览器支持与渐进增强
容器查询的浏览器支持已相当完善:
container-type,container,@container, 容器单位:Chrome 105+, Edge 105+, Safari 16+, Firefox 110+。- 样式查询:Chrome 111+, Edge 111+, Safari 18+, Firefox 暂不支持(截至 2025 年初)。
渐进增强策略: 先书写不依赖容器查询的基础样式(保证老旧浏览器的可用性),然后在 @supports 中编写增强样式:
.card {
display: block; /* 降级方案 */
}
@supports (container-type: inline-size) {
.card-container {
container-type: inline-size;
}
@container (min-width: 400px) {
.card {
display: flex;
}
}
}
10. 常见问题与最佳实践
- 性能:定义容器会触发 CSS Containment,浏览器可以更高效地计算布局,通常不会带来性能负担,反而可能提升渲染性能。
- 不要过度容器化:一个页面中存在过多容器查询可能增加维护复杂度,应优先为可复用的组件定义容器。
- 高度查询:使用
container-type: size并查询高度(@container (min-height: 200px))时,注意必须显式设置容器的高度,否则高度依赖内容,容易造成循环计算。通常情况谨慎使用。 - 调试工具:Chrome DevTools 会标记容器元素,并在样式面板中显示匹配的
@container规则,方便调试。
11. 总结
容器查询将响应式设计的控制权从视口转移到父容器,让组件真正实现“一次编写,处处运行”。它是构建现代设计系统、微前端和组件库的基石。掌握 container-type、@container 和容器相对单位,你的页面组件将变得更加灵活和可维护。
下一步建议:尝试在现有项目中为一个小组件加入容器查询,感受它与传统媒体查询在设计思维上的差异。