起因
老办法做 responsive:
.card { padding: 1rem; }
@media (min-width: 768px) {
.card { padding: 2rem; display: flex; }
}
依据viewport 调整。
但很多组件不关心 viewport,它关心自己父容器多大。
例:card 组件放 sidebar(300px)时纵向布局,放主区(800px)时横向。
media query 帮不上忙 → 同 viewport 不同位置要不同样式。
Container queries(CSS 2023+ 主流支持)解决:组件根据容器
自己大小调样式。
基本用法
父元素声明 containment:
.sidebar {
container-type: inline-size;
container-name: sidebar;
}
.main {
container-type: inline-size;
}
子元素 query 容器:
.card {
padding: 1rem;
display: block;
}
@container (min-width: 500px) {
.card {
padding: 2rem;
display: flex;
gap: 1rem;
}
}
card 在 sidebar (300px) 内 → block。
同 card 在 main (800px) 内 → flex。
containment type
inline-size:监听宽(最常用)size:宽 + 高normal:不监听(默认)
.parent { container-type: inline-size; }
注意:container-type 让元素成为 containment context,影响 layout
计算(不一定能在所有元素用)。
named container
.sidebar { container-type: inline-size; container-name: sidebar; }
.main { container-type: inline-size; container-name: main; }
@container sidebar (min-width: 400px) { ... }
@container main (max-width: 600px) { ... }
按 container 名 query,避免 ambiguity。
query unit (cqw / cqh)
.card {
font-size: 5cqi; /* container inline-size 的 5% */
padding: 2cqi;
}
cqi:container inline-size 1%cqb:container block-size 1%cqw/cqh:absolute width/height (less common)
字号跟容器大小成正比,缩放友好。
实战 example:可复用 card
.card {
container-type: inline-size;
border: 1px solid #ddd;
border-radius: 8px;
}
.card-inner {
padding: 1rem;
}
.card-thumb {
width: 100%;
aspect-ratio: 16/9;
}
@container (min-width: 400px) {
.card-inner {
display: grid;
grid-template-columns: 150px 1fr;
gap: 1rem;
}
.card-thumb {
width: 150px;
aspect-ratio: 1;
}
}
@container (min-width: 700px) {
.card-inner {
grid-template-columns: 200px 1fr;
}
.card-thumb {
width: 200px;
}
}
同一个 .card 在任何容器里自适应,不需要 media query 协调。
media query 仍有用
container query 不是 100% 替代 media query:
- 页面级布局(sidebar/main 切换)→ media query
- 组件内适应 → container query
- 基于设备特性(hover / touch) → media query
- prefers-color-scheme → media query
混用:
@media (prefers-color-scheme: dark) {
.card { background: #222; }
}
@container (min-width: 500px) {
.card { display: flex; }
}
style query (实验)
@container style(--theme: dark) {
.card { background: black; }
}
依据自定义 prop 值变样式。Chrome 111+,Safari 18+。
还在 stabilizing。
浏览器支持
Chrome 105+ / Safari 16+ / Firefox 110+ → 主流浏览器从 2022 末 / 2023
全支持。
2026 视角:可以默认用,老 browser 用 @supports fallback:
@supports not (container-type: inline-size) {
/* fallback to media query */
}
与 CSS-in-JS 对比
CSS-in-JS(styled-components / Emotion)经常通过 prop 控制样式:
<Card variant={width > 500 ? 'wide' : 'narrow'} />
JS 测宽 → re-render → 改样式。复杂 + JS 阻塞。
container query 纯 CSS,浏览器原生计算 → 更高效 + 简洁。
实际项目效果
我重构一个 dashboard,从 media query / JS measure 改 container query:
- 删 200+ 行 JS layout logic
- CSS 简化(不需要
at-768,at-1024等命名) - 同组件在 modal / sidebar / 主区 都能 work
- bundle 小 5KB
最大改善:组件真正可复用 —— 不需要为不同上下文写变体。
与 grid auto-fit / minmax 对比
/* grid 自适应 N 列 */
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
grid auto-fit 已经覆盖一些 container query 用例。
但 grid 是 layout 内自适应;container query 是组件内部样式。
踩过的坑
-
container-type: inline-size副作用:让元素 layout 隔离,
某些 height 计算变化。布局突变 → 检查父子。 -
嵌套 container:子 container 的 query 默认查最近的 named
container,不指定容易混乱。明确container-name。 -
inline element 不能 container-type:必须 block / inline-block 或者
display 至少能 contain。 -
@container在 nested rule 内:CSS nesting 里写时注意顺序。 -
devtool 难调:Chrome devtools 显示 container query 没 media
query 那么直观。仔细看 box model。
登录后参与评论。