媒体查询(Media Queries)基于视口宽度决定布局,组件样式与所在页面强耦合。同一个卡片组件放在侧边栏和主内容区需要不同布局,媒体查询无法区分这种场景。CSS Container Queries 让组件根据父容器宽度自适应,实现真正的组件级响应式设计。2023 年起主流浏览器全面支持,本文从语法到工程实践给出完整方案。
Container Queries与Media Queries的本质区别
媒体查询的响应条件是浏览器视口尺寸,所有组件共享同一套断点。当页面布局复杂时,侧边栏中的组件在宽屏下实际可用宽度可能只有 300px,却因视口宽度 1440px 而触发了”桌面”布局,导致内容溢出或留白。
容器查询将响应条件从视口转移到最近的祖先容器。组件根据自身实际可用空间决定布局,与页面整体宽度解耦。一个按钮组在 400px 容器中横向排列,在 200px 容器中自动切换为纵向堆叠,无需 JavaScript 介入。
container-type与容器上下文声明
使用容器查询前,需要将元素声明为查询容器:
/* 声明容器上下文 */
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
.main-content {
container-type: inline-size;
container-name: main;
}
/* 简写形式 */
.card-wrapper {
container: card / inline-size;
}
container-type: inline-size 表示以容器的内联尺寸(通常是宽度)作为查询维度。inline-size 是最常用值,因为大部分响应式布局关注宽度变化。size 同时查询宽高,但会建立尺寸包含(size containment),要求容器有明确尺寸,使用场景较少。
container-name 为容器命名,当组件被嵌套在多层容器中时,可通过名称指定查询哪个容器的尺寸。
@container查询语法与条件匹配
.card {
display: grid;
grid-template-columns: 1fr;
gap: 16px;
}
/* 当容器宽度 >= 400px 时切换为双列布局 */
@container (min-width: 400px) {
.card {
grid-template-columns: 200px 1fr;
gap: 24px;
}
}
/* 当容器宽度 >= 600px 时切换为三列布局 */
@container (min-width: 600px) {
.card {
grid-template-columns: 200px 1fr 1fr;
}
}
/* 命名容器查询 */
@container main (min-width: 800px) {
.card {
grid-template-columns: 1fr 1fr 1fr;
}
}
/* 范围查询语法(CSS Level 4) */
@container (400px <= width <= 600px) {
.card {
background: yellow;
}
}
容器查询支持 min-width、max-width 及范围语法。命名容器查询通过 @container main (min-width: 800px) 形式指定目标容器,适用于多个嵌套容器场景。
组件化实战:自适应卡片组件
一个完整的响应式卡片组件,根据容器宽度切换布局并调整字体大小:
<!-- HTML 结构 -->
<div class="page-layout">
<aside class="sidebar">
<article class="product-card">
<img class="product-card__image" src="product.jpg" alt="产品图" />
<div class="product-card__body">
<h3 class="product-card__title">无线降噪耳机</h3>
<p class="product-card__desc">主动降噪,40小时续航</p>
<div class="product-card__footer">
<span class="product-card__price">¥899</span>
<button class="product-card__btn">加入购物车</button>
</div>
</div>
</article>
</aside>
<main class="main-content">
<article class="product-card">
<!-- 同一组件结构 -->
</article>
</main>
</div>
/* CSS */
.page-layout {
display: grid;
grid-template-columns: 280px 1fr;
gap: 32px;
}
.sidebar {
container-type: inline-size;
}
.main-content {
container-type: inline-size;
}
/* 基础布局:窄容器下的纵向排列 */
.product-card {
display: flex;
flex-direction: column;
border-radius: 12px;
overflow: hidden;
background: #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.product-card__image {
width: 100%;
height: 180px;
object-fit: cover;
}
.product-card__body {
padding: 16px;
}
.product-card__title {
font-size: 16px;
margin: 0 0 8px;
}
.product-card__desc {
font-size: 14px;
color: #666;
margin: 0 0 12px;
}
.product-card__footer {
display: flex;
flex-direction: column;
gap: 8px;
}
/* 容器宽度 >= 300px:图片变水平排列 */
@container (min-width: 300px) {
.product-card {
flex-direction: row;
}
.product-card__image {
width: 140px;
height: auto;
min-height: 100%;
}
.product-card__body {
flex: 1;
}
.product-card__footer {
flex-direction: row;
align-items: center;
justify-content: space-between;
}
}
/* 容器宽度 >= 500px:加大间距和字体 */
@container (min-width: 500px) {
.product-card__image {
width: 200px;
}
.product-card__body {
padding: 24px;
}
.product-card__title {
font-size: 20px;
}
.product-card__desc {
font-size: 15px;
}
}
同一个 .product-card 组件在侧边栏(容器宽 280px)中显示为纵向卡片布局,在主内容区(容器宽 800px+)中自动切换为横向图文布局,无需为不同页面位置编写不同 CSS。
容器查询单位:cqw/cqh/cqi/cqb
容器查询单位类似于视口单位 vw/vh,但参照物是容器尺寸而非视口:
.hero-section {
container-type: inline-size;
}
.hero-title {
/* 10cqi = 容器内联尺寸的 10% */
font-size: 10cqi;
/* 最小不小于 1.5rem,最大不超过 4rem */
font-size: clamp(1.5rem, 10cqi, 4rem);
}
.hero-padding {
/* 5cqw = 容器宽度的 5% */
padding: 5cqw;
}
单位含义:cqw 容器宽度百分比,cqh 容器高度百分比,cqi 容器内联尺寸百分比,cqb 容器块尺寸百分比,cqmin/cqmax 取较小/较大值。结合 clamp() 实现流体排版,标题大小随容器宽度平滑变化且有上下限保护。
容器查询的浏览器兼容与降级方案
Container Queries 在 Chrome 105+、Safari 16+、Firefox 110+ 全面支持,覆盖 2026 年主流浏览器。针对旧浏览器的渐进增强方案:
@supports (container-type: inline-size) {
/* 容器查询支持的样式 */
@container (min-width: 400px) {
.product-card {
flex-direction: row;
}
}
}
@supports not (container-type: inline-size) {
/* 降级到媒体查询 */
@media (min-width: 768px) {
.sidebar .product-card {
flex-direction: column;
}
.main-content .product-card {
flex-direction: row;
}
}
}
也可使用 polyfill 方案如 container-query-polyfill 通过 ResizeObserver 实现降级支持,但 polyfill 有性能开销且不支持容器查询单位。推荐策略是对核心布局使用 @supports 降级,对增强效果仅在支持的浏览器中启用。
Tailwind CSS 3.4+ 内置容器查询支持,通过 @container 变体直接使用:
<div class="@container">
<div class="flex flex-col @md:flex-row @lg:gap-8">
<!-- 组件内容 -->
</div>
</div>
@container 标记容器上下文,@md:、@lg: 前缀对应不同容器宽度断点,与 md:、lg: 视口断点语法一致,降低心智成本。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/csscontainerqueries-rong-qi-cha-xun-shi-zhan-zu-jian-ji/