以前写响应式组件,总要根据视口宽度写一堆
@media,代码又臭又长。现在 Container Queries 让组件根据父容器尺寸响应,代码直接减半。本文讲透语法 + 5 个可直接复用的案例。
@media 媒体查询基于视口宽度。这导致一个组件在不同父容器下无法独立响应——同样的卡片,在侧边栏和主内容区需要两套样式或者额外类名。
Container Queries 允许你定义:当父容器达到某个宽度时,子元素改变布局。
/* 以前:基于视口,无法区分卡片在哪个容器里 */
@media (min-width: 600px) {
.card { display: flex; }
}
/* 现在:基于父容器宽度 */
@container (min-width: 300px) {
.card { display: flex; }
}
.sidebar {
container-type: inline-size; /* 监听内联方向(宽度)变化 */
container-name: sidebar; /* 可选,给容器起名 */
}
container-type: inline-size 最常用,表示根据宽度变化。size(宽高都监听)或 normal(不监听)。@container (min-width: 300px) {
.card-title {
font-size: 1.5rem;
}
}
如果只有一个容器,可以直接写条件。如果有多个容器,用 container-name 限定:
@container sidebar (min-width: 300px) {
/* 只对 sidebar 容器内的元素生效 */
}
@container (100px <= width < 300px) {
/* 容器宽度在 100px 到 300px 之间 */
}
| 场景 | 传统 @media 做法 | Container Queries 做法 |
|---|---|---|
| 卡片在侧边栏时垂直,在主区域时水平 | 写两套类名或全局判断 | 父容器定义后,卡片内一套规则自动适配 |
| 仪表盘小部件适应不同格子尺寸 | 根据视口断点统一控制 | 每个小部件独立响应自己的容器 |
代码对比:
<!-- 以前:需要额外类名 -->
<div class="sidebar">
<div class="card vertical"></div>
</div>
<div class="main">
<div class="card horizontal"></div>
</div>
/* 以前:手动控制 */
.card.vertical { flex-direction: column; }
.card.horizontal { flex-direction: row; }
/* Container Queries:一套规则 */
.card {
container-type: inline-size;
}
@container (min-width: 400px) {
.card { flex-direction: row; }
}
@container (max-width: 399px) {
.card { flex-direction: column; }
}
<div class="card-container">
<div class="card">
<img src="photo.jpg" alt="">
<div class="card-body">
<h3>标题</h3>
<p>描述文字</p>
</div>
</div>
</div>
.card-container {
container-type: inline-size;
}
.card {
display: flex;
flex-direction: column;
gap: 1rem;
}
@container (min-width: 350px) {
.card {
flex-direction: row;
align-items: center;
}
.card img {
width: 40%;
}
}
/* 不需要任何额外代码,卡片自己响应父容器宽度 */
.sidebar { container-type: inline-size; width: 280px; }
.main { container-type: inline-size; width: 800px; }
/* 卡片规则如上,当父容器<350px时垂直,>350px时水平 */
<div class="dashboard">
<div class="widget" style="width: 200px">...</div>
<div class="widget" style="width: 400px">...</div>
<div class="widget" style="width: 200px">...</div>
</div>
.widget {
container-type: inline-size;
}
.widget-content {
font-size: 12px;
}
@container (min-width: 250px) {
.widget-content {
font-size: 16px;
display: flex;
gap: 1rem;
}
}
.product-list {
container-type: inline-size;
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
.product-item {
flex: 1 1 200px;
}
@container (max-width: 500px) {
.product-item {
flex-basis: 100%; /* 小于500px时每行一个 */
}
}
<nav class="navbar">
<div class="logo">Logo</div>
<ul class="nav-links">
<li>首页</li>
<li>产品</li>
<li>关于</li>
</ul>
</nav>
.navbar {
container-type: inline-size;
display: flex;
justify-content: space-between;
}
.nav-links {
display: flex;
gap: 1rem;
}
@container (max-width: 500px) {
.navbar {
flex-direction: column;
}
.nav-links {
flex-direction: column;
margin-top: 0.5rem;
}
}
支持情况(截至 2026-06):
如果需要兼容旧浏览器,使用 @supports 渐进增强:
/* 降级:默认样式(例如始终垂直) */
.card {
flex-direction: column;
}
/* 支持 Container Queries 时覆盖 */
@supports (container-type: inline-size) {
.card-container {
container-type: inline-size;
}
@container (min-width: 350px) {
.card {
flex-direction: row;
}
}
}
container-type + @container。@supports 安全降级。文中所有代码均可直接复制到项目中。以后写响应式组件,先问自己:这个样式是由视口决定,还是由父容器决定?
讨论:你在开发中遇到过哪些因 @media 全局断点导致的组件复用困难?欢迎留言分享。