图在上、文在下(默认样式)
命中 @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| 单位 | 相对什么 |
|---|---|
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() 的组合:真的够用了:has():下面这个 stack 在 420px 以上横排,但当它有 ≥4 项时改成两列网格@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; }
/* 只有支持时才改变形态 */
}