CSS Container Queries 实战:告别媒体查询,真正让组件自适应
CSS Container Queries 实战:告别媒体查询,真正让组件自适应
做前端的都知道,写响应式布局这么多年,我们一直在用 @media(媒体查询)来调样式。但你有没有遇到过这种情况:一个卡片组件在侧边栏里要竖排,到了主内容区就得横排,然后你就在那疯狂写覆盖样式,最后搞得 CSS 又臭又长。
这就是老方案的硬伤——媒体查询只能看视口大小,根本不关心你组件的爹是谁。
所以我今天要聊的,就是一个能从根本上解决这个问题的方案:CSS Container Queries,容器查询。
它到底好在哪
先看对比数据。传统的媒体查询写法,大概是这样的:
/* 基于视口宽度的媒体查询 */
@media (min-width: 768px) {
.card { display: flex; flex-direction: row; }
}
@media (max-width: 767px) {
.card { display: flex; flex-direction: column; }
}
看着没啥问题对吧?但是,这个 .card 组件如果放在 300px 宽的侧边栏里,768px 的视口下它其实只有很小的地方,但媒体查询还是会把它变成横排。你想让它自适应,就得多写一套 .sidebar .card 的覆盖样式。
用了 Container Queries 之后:
/* 定义容器 */
.card-wrapper {
container-type: inline-size;
container-name: card;
}
/* 根据容器宽度自适应 */
@container card (min-width: 400px) {
.card { display: flex; flex-direction: row; }
}
@container card (max-width: 399px) {
.card { display: flex; flex-direction: column; }
}
同一个 .card,不管放在侧边栏还是主内容区,自动根据父容器的实际宽度来切换布局。这就是真正的组件级响应式。
三个核心概念,一篇文章讲透
要上手 Container Queries,你只需要搞明白三个东西:container-type、container-name 和 @container 规则。
container-type:谁来做容器
这个是声明”我要把我的某个元素变成容器”。有三个值:
.container {
/* 只关注内联方向(一般是宽度),最常用 */
container-type: inline-size;
/* 宽度和高度都关注,但会触发 contain,慎用 */
/* container-type: size; */
/* 不做容器查询, 默认值 */
/* container-type: normal; */
}
日常 90% 的场景只用 inline-size 就够了。size 不仅要管高度,还会强制开启 contain,有时会引发布局异常,非特殊需求别用。
container-name:给容器起个名
如果你的页面里有很多容器,嵌套着用,就需要给它们各起个名,不然查询时就懵了。
.sidebar-panel {
container-type: inline-size;
container-name: sidebar;
}
.main-content {
container-type: inline-size;
container-name: main;
}
简写语法更清爽:
.card-container {
container: card / inline-size;
}
@container:真正发力的查询
语法和 @media 几乎一样,但它查的是容器尺寸。
@container card (min-width: 500px) {
.card-title { font-size: 1.5rem; }
.card-body { display: grid; grid-template-columns: 1fr 1fr; }
}
@container card (max-width: 499px) {
.card-title { font-size: 1rem; }
.card-body { display: flex; flex-direction: column; }
}
注意!这里的 min-width 是指容器 .card-container 的宽度,不是视口宽度。这个区别就是整个 Container Queries 的灵魂。
浏览器兼容性(2026 年现状)
截止 2026 年中,Container Queries 已经得到所有主流浏览器的全面支持:
| 浏览器 | 支持版本 |
|---|---|
| Chrome 105+ | ✅ 完全支持 |
| Firefox 110+ | ✅ 完全支持 |
| Safari 16+ | ✅ 完全支持 |
| Edge 105+ | ✅ 完全支持 |
所以别担心兼容性了,直接干就行。
实战一:响应式卡片组件
这是最经典的场景。一个卡片在宽容器里横排,窄容器里竖排。
/* 定义容器 */
.card-container {
container: card-item / inline-size;
}
/* 卡片基础样式 */
.card-item {
display: flex;
flex-direction: column; /* 默认竖排 */
gap: 1rem;
padding: 1rem;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}
.card-image {
width: 100%;
border-radius: 8px;
}
/* 容器宽度 >= 400px 时变横排 */
@container card-item (min-width: 400px) {
.card-item {
flex-direction: row;
align-items: center;
}
.card-image {
width: 200px;
flex-shrink: 0;
}
}
/* 容器宽度 >= 700px 时更大气的排版 */
@container card-item (min-width: 700px) {
.card-item {
padding: 2rem;
}
.card-title { font-size: 1.75rem; }
.card-image { width: 300px; }
}
你看,一个 .card-item,放在 300px 宽的侧边栏、500px 宽的内容区、800px 宽的页面主体里,分别展示不同的布局——完全自动,不需要额外写任何覆盖样式。
实战二:Dashboard 自适应网格
Dashboard 是最适合 Container Queries 的场景之一。不同宽度的 widget 块自动调整内部布局。
.widget {
container: widget-box / inline-size;
border-radius: 16px;
background: #fff;
padding: 1rem;
}
/* 小 widget:紧凑 */
@container widget-box (max-width: 300px) {
.widget-header { font-size: 0.875rem; }
.widget-chart { height: 120px; }
.widget-data { display: none; }
}
/* 中 widget:标准 */
@container widget-box (min-width: 301px) and (max-width: 600px) {
.widget-header { font-size: 1rem; }
.widget-chart { height: 200px; }
}
/* 大 widget:完整版 */
@container widget-box (min-width: 601px) {
.widget-header {
font-size: 1.25rem;
display: flex;
justify-content: space-between;
}
.widget-chart { height: 300px; }
.widget-data { display: grid; grid-template-columns: repeat(3, 1fr); }
}
这个模式在 Grafana、Datadog 那种监控面板里特别好使。一个组件三套样式,完全自动切换。
实战三:结合 CSS 容器查询单位
Container Queries 还带来了一组新单位,用起来贼爽:
| 单位 | 含义 |
|---|---|
| cqw | 容器宽度的 1% |
| cqh | 容器高度的 1% |
| cqi | 容器内联方向尺寸的 1% |
| cqmin | cqi 和 cqb 中较小的那个 |
| cqmax | cqi 和 cqb 中较大的那个 |
.widget-content {
container: widget-body / inline-size;
}
@container widget-body (min-width: 400px) {
.metric-value {
/* 字体大小随容器宽度自适应 */
font-size: clamp(1.5rem, 5cqi, 3rem);
}
.metric-unit {
font-size: 0.6em;
margin-left: 0.25em;
}
}
这里的 5cqi 就是容器宽度的 5%,配合 clamp() 既能自适应,又限制了大小范围,简直完美。
进阶:嵌套容器查询
Container Queries 可以嵌套使用,每个层级的容器只查自己的父容器。
/* 外层容器 */
.page-section {
container: section / inline-size;
}
/* 内层卡片容器 */
.card-wrapper {
container: card-inner / inline-size;
}
/* 根据 section 宽度调整卡片列数 */
@container section (min-width: 900px) {
.card-grid {
grid-template-columns: repeat(3, 1fr);
}
}
@container section (max-width: 899px) {
.card-grid {
grid-template-columns: repeat(2, 1fr);
}
}
/* 根据卡片自身容器宽度调整卡片内部布局 */
@container card-inner (min-width: 350px) {
.card-item { flex-direction: row; }
}
这样,页面布局层级的响应式交给外层容器管,组件内部的响应式交给内层容器管,各司其职。
和媒体查询怎么分工?
Container Queries 虽然强,但不是来取代 @media 的。它俩有明确的分工:
媒体查询负责全局布局——几行网格、页面上下间距、菜单是否折叠,这些由视口决定的事,还是 @media 干。
容器查询负责组件自适应——卡片、面板、表格、列表等组件根据容器空间自动调整,这才是 Container Queries 的战场。
我现在的习惯写法是:
/* 全局布局:用媒体查询 */
@media (min-width: 1200px) {
.layout-main { grid-template-columns: 280px 1fr; }
}
@media (max-width: 1199px) {
.layout-main { grid-template-columns: 1fr; }
}
/* 组件内部:用容器查询 */
.widget-container {
container: widget / inline-size;
}
@container widget (min-width: 500px) {
/* 组件自适应的样式 */
}
一个管页面的骨架层级,一个管组件的血肉层级,分工清楚,各干各的。
踩过的几个坑
用了半年多,也踩了不少坑,给你提个醒:
1. 容器本身不要用 @container 改布局。 容器的尺寸变化不能被自身的 @container 感知,这会导致死循环。容器的样式改用媒体查询控制。
2. container-type: size 要慎用。 它需要用 contain: layout style size,可能会导致绝对定位的子元素错位。没特殊需要时,一律用 inline-size。
3. Firefox 旧版本的 contain 问题。 114 之前的版本会遇到 contain 和 Canvas 2D 的冲突。不过现在最新版已经修了。
4. DevTools 调试。 Chrome DevTools 已经支持了,Elements 面板里找到容器元素,会显示一个 container 标签,点它就能看到所有 @container 规则和当前命中的断点状态。
写在最后
Container Queries 是我 2026 年最推荐的 CSS 新特性之一。如果说 CSS Grid 解决了二维布局的问题,那 Container Queries 解决的就是”组件在哪里都能活得很好”的问题。
以前写一个组件要考虑它在页面的每个位置长什么样,现在只管定好容器,组件自己会适配。这种”组件自治”的思路,跟组件化开发的理念天然吻合。
现在浏览器全面支持了,真没什么理由不上了。找个项目里的卡片或者面板,试试换 Container Queries 重写,你就知道有多爽。
评论区
登录后可评论。












