容器查询:组件按「我被放进多大的地方」来变化,而不是按屏幕多大

① 两条语句就够

窄容器里

图在上、文在下(默认样式)

只显一行

宽一点的容器

命中 @container (min-width: 340px) 后变成图左文右,标题字号也变大。

标签日期阅读时长
/* 一、把某个祖先声明为查询容器 */
.card-zone { container-type: inline-size; container-name: cardzone; }
/* 二、子树内部按它的尺寸写规则(注意查询的是祖先,样式给后代) */
@container cardzone (min-width: 340px) { .media { grid-template-columns: 128px 1fr; } }

/* 简写:container: inline-size / cardzone; */
/* 匿名容器:不写 name 时也能用 @container (min-width: 340px),但要小心命中了「更近的」某个容器 */

为什么这比媒体查询重要:同一个卡片可以放进侧栏(窄)、主区(中)、整页 banner(宽),组件源码完全相同,表现由容器决定。这才是「组件级响应式」。

② 容器单位:cqi / cqb / cqmin / cqmax

8cqi
padding: 2cqi 3cqi
8cqi
同样的 CSS,更小的容器
单位相对什么
cqi容器内联轴(水平书写模式=宽度)的 1%
cqb容器块轴(=高度)的 1%,需要 container-type: size 或 block
cqw / cqh宽度/高度的 1%(等价于横向 writing-mode 下的 cqi/cqb)
cqmin / cqmax两者中较小/较大者的 1%
.title { font-size: 8cqi; }             /* 字号随容器宽度线性变化,不需要 clamp 猜视口 */
.card  { padding: 2cqi; }
/* 与 vw 的关系:vw 相对视口,cqi 相对最近的尺寸容器 —— 组件内部一律用后者 */

③ 三种 container-type 与它们的代价

值可查询副作用
inline-size只有宽度轴最常用:不影响元素自身高度,不产生循环依赖
size两个轴容器高度必须与内容无关(否则「内容决定高度、高度又决定内容」的循环)—— 通常要显式给 height,示例里左上的紫底方块就是这种情况
normal不查询默认,取消容器身份

声明容器还会带来两件事:container-type 非 normal 时元素会建立独立格式化上下文(内部 margin 不再与外部塌陷),并且会成为绝对定位后代的包含块(第十二章)。

④ 与媒体查询的分工

问题用什么
整页骨架变化(三栏 → 一栏、导航 → 汉堡)@media:这是页面级事实,只有视口能回答
组件在不同槽位里的形态(同一卡片在侧栏/主区)@container:组件不该关心视口
卡片墙要几列auto-fit + minmax()(第十一章)——能不用查询就不用
字号随所在容器缩放cqi 单位
系统偏好(深浅色、减少动效)@media 特性查询(第十三章)
/* 三者可以叠:外层用媒体查询定骨架,内层用容器查询定组件形态 */
@media (min-width: 60rem) { .page { grid-template-columns: 216px 1fr 232px; } }
@container main (min-width: 40rem) { .post { font-size: 1.0625rem; } }

⑤ 与 :has() 的组合:真的够用了

@container (min-width: 420px) {
  .list { grid-auto-flow: column; grid-auto-columns: 1fr; }
  .list:has(> :nth-child(4)) { grid-auto-flow: row; grid-template-columns: repeat(2, 1fr); }
}

「容器够宽就横排,但项数 ≥4 时改两列」——这条需求在两年前必须写 JS,现在两条 CSS 规则。

⑥ 浏览器基线与降级

@container 与容器单位已在 Chrome / Edge / Safari / Firefox 全量支持(Firefox 110 起),属 Baseline 可用。仍要担心旧内核时,用 @supports (container-type: inline-size) 包住容器规则,让旧浏览器落回「默认(窄)形态」——默认样式必须自身成立,这是渐进增强的前提。

@supports (container-type: inline-size) {
  .zone { container-type: inline-size; }
  /* 只有支持时才改变形态 */
}