Smashing Magazine

Stop Treating CSS Container Queries Like Traditional Media Queries

8.5内容质量
Stop Treating CSS Container Queries Like Traditional Media Queries

TL;DR · AI 摘要

CSS容器查询与媒体查询本质不同,正确使用能提升组件适应性,但当前仅41.4%开发者实际应用。

核心要点

  • 容器查询关注组件容器尺寸,媒体查询关注视口尺寸
  • State of CSS调查显示86%开发者知晓但仅41.4%使用容器查询
  • 错误使用容器查询会导致组件响应性失效

结构提纲

按章节快速跳转。

  1. 揭示容器查询使用率低但浏览器支持度高的矛盾现象

  2. 明确容器查询与媒体查询在响应机制上的本质区别

  3. 分析开发者将容器查询当作媒体查询使用的常见错误

  4. 演示如何通过容器查询实现组件自适应布局

  5. 引用State of CSS调查数据佐证行业现状

思维导图

用一张图看清主题之间的关系。

查看大纲文本(无障碍 / 无 JS 友好)
  • CSS容器查询解析
    • 与媒体查询的区别
      • 响应对象不同(容器vs视口)
      • 作用范围不同(局部vs全局)
    • 使用场景
      • 可复用组件自适应
      • 嵌套布局响应
    • 常见误区
      • 当作媒体查询使用
      • 忽略容器层级影响

金句 / Highlights

值得收藏与分享的关键句。

#CSS#前端#响应式设计#容器查询
打开原文

停止将CSS容器查询当作传统媒体查询来使用 —— Smashing Magazine

  • 2026年9月16日
  • 0条评论

停止将CSS容器查询当作传统媒体查询来使用

  • 12分钟阅读时间
  • 在Twitter上分享,在LinkedIn上分享

#### 关于作者

我编写代码。我撰写文章。表情包规则。好吧,认真说,我是一名Web和移动开发人员,经常写作。在空闲时间,我喜欢构建一些产品……更多关于Victor的信息 ↬

#### 电子邮件通讯

每周前端与用户体验技巧。182,000+人信任。

  • 与Rémi Parmentier一起构建现代HTML电子邮件
  • 庆祝1000万开发者
  • 实时UX培训 —— 与Vitaly Friedman一起学习智能界面设计模式
  • Figma MCP与Christine Vallaure
  • 与Stéphanie Walter一起学习设计师的无障碍设计

尽管浏览器支持广泛,容器查询的使用率却出人意料地低,且经常被误解。让我们看看它们与媒体查询有何不同,何时应使用它们,以及容器查询如何帮助可重用组件自然地响应其出现的上下文。

坦白说,当CSS容器查询首次发布时,我错过了这个消息。当我最终听说它时,我的第一反应是:“既然已经有媒体查询了,我为什么还需要这个?”

现在我知道了,我并不为这种反应感到自豪,但知道我不是唯一有这种想法的人,这让我感到一丝安慰。事实上,我们中有很多人都是如此。

令我感到困惑的是,容器查询并不是一个新功能,目前浏览器支持率约为94%。然而,真正使用它的人却非常少。根据CSS现状调查,86%的开发者了解容器查询,但只有41.4%的人实际使用它们。调查可能存在偏差,无法完全代表整个领域,但这是目前我们能获得的最佳指标。

Kevin Powell在2026年SmashingConf阿姆斯特丹会议上也谈到了这一点:容器查询的采用情况非常糟糕。这对我来说非常奇怪,因为多年来,组件能够根据其外部容器的大小进行调整一直是许多CSS愿望清单的首要事项。

我并不特别关注有多少人使用容器查询,而是更关注他们是如何使用的。我无法涵盖所有人,但从我所看到的情况来看——包括我自己的早期尝试——我们中有很多人使用方式是错误的。

关键问题是,错误的使用方式源于我最初学习它们时的相同印象:它们在第一眼看起来与媒体查询完全一样。由于它们看起来相似,人们很容易假设它们的目的和工作方式类似。

它们并不一样。

注意:我需要在本文开头明确说明,我关注的是使用容器尺寸查询,即一种响应式设计技术,用于响应特定容器的大小。还有容器样式查询,它们响应容器的计算样式(在本文撰写时仍处于实验阶段)。你可以在Smashing Magazine上Juan Diego的文章中了解这些内容,他探讨了它们的潜在使用场景。

code
@media (min-width: 1024px) {
  .card {
    display: flex;
  }
}

和大多数开发者一样,我一直在向浏览器提问:"当前屏幕的宽度是多少?" 就是这样。

媒体查询优雅地回答了这个问题,但当这个 .card 组件被放置在一个 1920px 桌面屏幕的 300px 宽的网格单元格中时会发生什么呢?

媒体查询并不关心这些,它只是完成自己的工作。视口仍然是 1920px,所以 min-width: 1024px 依然会触发,匹配的查询样式会被应用,即使卡片组件只有 300px 的可用空间。最终,卡片中的所有内容都会发生形变、溢出或拥挤。

"媒体查询是愚蠢的。不是说概念层面的愚蠢,而是它们对很多东西都一无所知。事实上,大多数人误以为它们知道得比实际更多。" —— Kevin Powell

我们通常会认为响应式设计纯粹是一个更新完整页面布局的系统,比如从大屏幕的双列布局切换到小屏幕的单列布局。

容器查询关注内部

容器查询比这更聪明。它们使响应式布局更依赖组件内部发生的情况,而不是依赖无法洞察组件内容的外部上下文。更像是在问:"我现在在这个特定位置有多少可用空间?"

这是我们在上一节看到的相同卡片代码示例,但这次使用了容器查询:

code
.card-wrapper {
  container-name: card;
  container-type: inline-size;
}

@container card (min-width: 450px) {
  .card {
    display: flex;
    flex-direction: row;
  }
}

这改变了所有规则。卡片不再受视口影响;它唯一关心的是其父级包装器是否至少有 450px 的内联(即从左到右书写模式中的水平)空间。如果条件成立,组件会采用水平布局;否则,会使用默认的块级显示。

查看 Vayo 的 [Viewport vs Container [forked]](https://codepen.io/smashingmag/pen/01a0a73d-6f39-709f-8dae-037f6b588852) 示例。

查看 Vayo 的 Viewport vs Container [forked] 示例。

"宏观"布局 vs. "微观"布局

区分 @media 和 @container 查询的一个非常有趣的方式是布局类型。

媒体查询用于"宏观"布局;它们关注外部因素。比如页面结构、横跨窗口的页眉、页脚、主网格布局、系统偏好(prefers-color-scheme)、设备能力(触摸屏)等。这些都属于对整个页面结构有重大影响的因素。

我认为容器查询更适合"微观"布局,即存在于"宏观"布局中的大多数元素。当我们需要任何内容根据分配的空间进行响应式调整时,就会使用它们。让人联想到的组件包括卡片、小部件、表单、导航等。

换句话说,就是"页面布局"与"组件布局"之间的区别。

一个元素不应该仅仅因为宽度超过像 768px 这样的抽象阈值就神奇地变成"平板尺寸"。相反,它应该在有足够的空间时切换布局,无论这种空间是在移动设备视口还是桌面侧边栏中出现。

如果你仍然不相信,你知道现代网络上有超过 2300 种独特的视口尺寸吗?你认为有可能为所有这些尺寸都做出适配吗?

我并不是在否定媒体查询。问题在于,在这个响应式和组件复用的时代,布局逻辑更应贴近容器而非视口。当我们以容器和组件为思考单位时,实际上是在依赖内容来决定布局,而不是视口。

这正是它应有的样子。

示例:组件内部的流体排版

响应式排版是许多人都依赖媒体查询实现的一个典型例子。媒体查询自带的 CSS 单位(如 vw、vh 等)与视口尺寸相关,这让媒体查询在根据用户屏幕尺寸调整字体大小时显得非常合适。

code
.card-title {
  font-size: clamp(100%, 1rem + 2vw, 24px);
}

但当这个组件被移动到不同的上下文中,例如侧边栏时,视口就变得毫无意义了。此时,由于我们将响应性绑定到了错误的参考点,排版可能会变得过大或过小。

容器查询自带了专属的单位(如 cqi、cqw、cqb 等),我们可以结合这些单位与 CSS 的 clamp() 函数,实现更进一步的响应式组件,让排版根据组件自身进行缩放,而不是依赖视口:

code
.card-title {
  font-size: clamp(1rem, .5rem + 3cqi, 2rem);
}

查看 Vayo 的 [Fluid Typography [forked]](https://codepen.io/smashingmag/pen/01a0a73f-1a44-750c-8b40-9b0ab774d981) 示例。

查看 Vayo 的 Fluid Typography [forked] 示例。

通过这种方式,整个代码完全自包含于该元素的特定容器中。

示例:Flexbox 包裹检测

有趣的是,容器查询在某种程度上可以检测组件内部布局的状态。虽然这并非完全可靠,但这是媒体查询无法做到的,因为媒体查询只能观察外部浏览器窗口,对内部布局事件(例如 flex 项目换行)完全无能为力。让我们深入探讨这一点。

Flexbox 在内容包裹(flex-wrap: wrap)方面表现出色,当 flex 容器空间不足时,可以让 flex 项目自动换行到新行。但 CSS 本身无法检测到这种换行的发生。目前没有类似 :wrapped 伪类或 @media (flex-wrapped: true) 这样的媒体查询来实现这一功能。

媒体查询只能观察浏览器窗口,因为它们无法看到内部变化。实际上,flex 换行本质上就是项目自身的宽度状态变化。

例如,如果你有一个用 flexbox 布局的水平菜单,并希望在项目换行时才重新样式化它们,你将不得不使用 JavaScript 和 ResizeObserver 来获取相关信息。但当我们把容器查询嵌套在 flex 项目内部时,可以借助 Kevin Powell 的技巧,无需 JavaScript 即可实现这一目标。核心思路是:当查询容器的内联尺寸时,允许 flex 项目使用 flex-grow: 1。

(大图预览)

其逻辑如下:

  • 当空间充足时,两个项目(.flex-item)并排显示,每个项目宽度正好是父容器宽度的一半。
  • 当空间有限时,第二个项目换行到下一行。
  • 由于每个项目都启用了 flex-grow,换行后的项目会拉伸以填充父容器的大部分宽度。
  • 如果该项目本身是一个容器,它会检测到宽度的突然扩展并触发响应。
code
/* 弹性容器父元素 */
.flex-layout {
  display: flex;
  flex-wrap: wrap;
}

/* 将弹性项目注册为容器 */
.flex-item {
  container-type: inline-size;
  flex: 1 1 390px; /* 扩展以填充空间,在390px时换行 */
}

/* 默认卡片样式(窄版/并排显示) */
.card {
  display: flex;
  flex-direction: column;
  background: #f4f4f4;
}

/* 当有足够的空间容纳完整行时 */
@container (min-width: 600px) {
  .card {
    flex-direction: row;
    align-items: center;
    background: #e2f0d9;
  }
}

这可以正常工作。当父容器尺寸缩小且卡片换行到两行时,卡片项会扩展,容器查询会触发并应用必要的样式。

查看 Vayo 的 CodePen 示例:使用容器查询进行弹性换行检测(已分叉)

查看 Vayo 的示例:使用容器查询进行弹性换行检测(已分叉)

容器查询确实存在副作用

与所有技术一样,容器查询也存在一些需要注意的副作用或限制。

1. 容器需要额外的包装元素

为了按预期工作,容器查询需要类似父-子关系的结构。假设我们有以下结构:

code
<div class="card">
  <div class="card-content">...</div>
</div>

我们无法直接查询 .card 组件来调整 .card-content 的样式,例如这样:

code
/* 不起作用 */
.card {
  container-name: card;
  container-type: inline-size;
}

@container card (min-width: 400px) {
  .card {
    display: flex;
  }
}

这不起作用,因为容器无法查询自身。在上一个示例中,我们正在查询一个卡片容器,然后试图根据其尺寸调整该容器的 display 属性。这会导致无限循环。

相反,我们需要添加一个额外的包装元素,使 .card 成为容器的后代:

code
<div class="cards">
  <div class="card">
    <div class="card-content">...</div>
  </div>
</div>

从这里开始,我们可以查询 .cards 容器并相应地调整 .card 的布局:

code
.cards {
  container-name: cards;
  container-type: inline-size;
}

@container cards (min-width: 400px) {
  .card {
    display: flex;
  }
}

在媒体查询中,这不会产生影响,因为 @media 不关心你在块中样式化的元素;它只关注视口,而视口始终可用。因此你可以随意在任何元素上添加条件并完成工作。

2. 查询容器尺寸可能导致布局崩溃

当查询容器的块尺寸(即垂直尺寸)而不是 inline-size 时会发生这种情况:

code
/* 即使内部有内容也会折叠为0px */
.hero-banner {
  container-type: size;
}

为什么会这样?因为浏览器在计算容器尺寸时不会考虑其子元素。如果我们没有为 .hero-banner 显式指定高度(或 min-height 或 aspect-ratio),浏览器会将其高度设置为 0px。

因此,通常建议通过 inline-size 查询容器,除非你确实需要查询容器的块尺寸。

媒体查询不受此影响,因为它们处理高度的方式与 @media (min-height: ...) 一样,即询问视口并继续处理。

3. 查询无法使用自定义属性

另一个容器查询限制:我们无法根据自定义属性值进行查询:

code
:root {
  --breakpoint-lg: 1600px;
}

/* 无法正常工作 */ @container (min-width: var(--breakpoint-lg)) { /* ... */ }

code

这是因为自定义属性依赖于沿DOM树传递的值。存在一种可能性:依赖自定义属性的容器查询可能会改变该自定义属性本身。这会迅速变得复杂:

:root { --breakpoint-lg: 1600px; }

/* 无法正常工作 */ @container cards (min-width: var(--breakpoint-lg)) { .card { --breakpoint-lg: 1000px; } }

code

## 何时使用媒体查询和容器查询

我认为没有任何项目应该完全用@container替代@media。媒体查询在响应式布局中仍然发挥着重要作用。关键在于理解关注点分离的原则。

当组件在多个布局场景中使用时,我倾向于使用容器查询。例如,.card元素可能出现在全宽网格或较窄的侧边栏中。如果是这种情况,我们希望组件内容本身决定何时调整样式,而不是依赖查看整个视口的媒体查询。

相反,当组件仅存在于页面层级时,我会选择使用媒体查询。比如始终位于页面顶部的主要导航栏。它直接受视口尺寸影响,这意味着视口是导航栏需要调整时的可靠参考。同样,这涉及"宏观"布局与"微观"布局的区别。

这是我判断使用哪种查询类型的示意图:

## 结论

归根结底,容器查询与媒体查询看起来非常相似的核心原因仅仅是熟悉度。它们并非"全新",但相比媒体查询,人们对容器查询的理解和采用程度要低得多。但媒体查询本身也有诸多限制,否则我们就不需要容器查询来填补这些空白。

我们拥有的是一个更有效的功能:可以检测特定组件上下文何时发生变化,并根据其内容调整样式,这正是当该组件可以存在于多种上下文时应有的表现方式。

(gg, yk)

Smashing Newsletter 前端与UX技巧,每周发送到你的邮箱。只包含真正实用的内容。

前端与UX工作坊,在线参与。包含实用收获、现场课程、视频录像和友好的问答环节。

TypeScript 50课 详解TypeScript,包含代码讲解和示例。以及其他印刷书籍。