CSS容器查询(Container Queries)是响应式布局领域的重大演进。传统媒体查询(@media)基于视口宽度判断断点,导致同一组件在不同页面位置需要写多套样式规则。容器查询基于父容器宽度判断,使组件样式只取决于自身容器尺寸,与视口大小解耦。这一特性让真正的组件级响应式设计成为可能,组件可以在任何布局上下文中自适应渲染,无需关心外层页面结构。
容器查询基础:@container与container-type属性
容器查询的使用分两步:先在父元素上声明容器上下文,再在子元素上用@container规则写条件样式。container-type: inline-size表示容器沿inline轴(通常是水平方向)的尺寸用于查询判断。
.card-wrapper {
container-type: inline-size;
/* 或使用简写: */
container: card / inline-size;
}
@container card (min-width: 400px) {
.card {
display: flex;
flex-direction: row;
}
.card__image {
width: 200px;
flex-shrink: 0;
}
}
@container card (max-width: 399px) {
.card {
display: flex;
flex-direction: column;
}
.card__image {
width: 100%;
aspect-ratio: 16 / 9;
}
}
container-type的取值包括normal(默认,不作为容器)和inline-size(按inline轴尺寸查询)。size值同时查询两个轴,但会禁用容器的size containment,影响布局性能,实际使用较少。container-name为容器命名,在嵌套多层容器时用于精确指定查询目标。
.layout {
container: layout / inline-size;
}
.sidebar {
container: sidebar / inline-size;
}
/* 查询最近的指定名称的容器 */
@container layout (min-width: 1200px) {
.sidebar { width: 300px; }
}
@container sidebar (min-width: 250px) {
.widget { font-size: 14px; }
}
媒体查询的局限性与容器查询解决场景
媒体查询的核心问题在于它绑定视口尺寸,而非组件自身可用空间。一个卡片组件放在全宽页面主区域和放在300px侧边栏时,视口宽度相同但可用空间差异巨大,媒体查询无法区分这两种情况。
典型场景:一个新闻卡片组件,在主页三列网格中宽度约380px,在文章详情页侧边栏中宽度约280px,在全宽横幅中宽度约800px。使用媒体查询需要写三套独立的CSS规则,并依赖外层class区分场景。使用容器查询只需一套规则,组件根据容器宽度自动切换布局。
/* 容器查询方案:组件不关心自己在哪里 */
.card-container {
container-type: inline-size;
}
@container (width >= 600px) {
.news-card {
display: grid;
grid-template-columns: 240px 1fr;
gap: 24px;
}
.news-card__media { aspect-ratio: 4 / 3; }
.news-card__title { font-size: 22px; }
}
@container (width < 600px) and (width >= 350px) {
.news-card {
display: flex;
flex-direction: column;
}
.news-card__media { aspect-ratio: 16 / 9; }
.news-card__title { font-size: 18px; }
}
@container (width < 350px) {
.news-card {
display: flex;
flex-direction: row;
gap: 12px;
}
.news-card__media {
width: 80px;
height: 80px;
border-radius: 8px;
flex-shrink: 0;
}
.news-card__title { font-size: 14px; }
.news-card__excerpt { display: none; }
}
这套CSS无论组件被放置在哪个位置,都会根据实际容器宽度选择最优布局。组件开发者只需关注容器尺寸,页面组装者只需提供合适的容器宽度,职责清晰分离。
组件级响应式布局重构实践
从媒体查询迁移到容器查询时,核心改动是将组件最外层元素设置为container,然后将组件内部的@media规则改为@container规则。迁移过程中需要注意容器尺寸的计算方式:容器的inline-size是其content-box的inline尺寸,padding和border不计入。
/* React组件示例 */
function ProductGrid({ products }) {
return (
<div className="grid-container">
{products.map(p => (
<div className="product-card" key={p.id}>
<img src={p.image} alt={p.name} />
<h3>{p.name}</h3>
<p className="price">¥{p.price}</p>
</div>
))}
</div>
)
}
.grid-container {
container-type: inline-size;
display: grid;
gap: 16px;
}
@container (width >= 800px) {
.grid-container {
grid-template-columns: repeat(3, 1fr);
}
.product-card .price { font-size: 20px; }
}
@container (width >= 450px) and (width < 800px) {
.grid-container {
grid-template-columns: repeat(2, 1fr);
}
}
@container (width < 450px) {
.grid-container {
grid-template-columns: 1fr;
}
}
grid容器本身也是查询目标,@container查询的是grid-container的inline-size。当grid容器被放置在不同宽度的父布局中时,列数自动调整,无需JS监听或媒体查询。
container-style查询与样式条件判断
CSS Container Queries Level 3规范引入了style queries,允许基于容器的自定义属性值进行条件查询。这为基于设计Token的主题切换提供了原生CSS方案。
.theme-container {
container-type: inline-size;
container-name: theme;
--card-style: "compact";
}
@container theme style(--card-style: "compact") {
.card {
padding: 8px;
gap: 4px;
}
}
@container theme style(--card-style: "comfortable") {
.card {
padding: 24px;
gap: 16px;
}
}
/* 通过JS动态切换风格 */
// document.querySelector('.theme-container')
// .style.setProperty('--card-style', 'comfortable')
style queries目前浏览器支持尚不完整(Chrome 111+支持,Firefox和Safari处于实验阶段)。在不支持的环境中,CSS解析器会忽略不认识的@container规则,不会报错,属于渐进增强范畴。
浏览器兼容性处理与渐进增强方案
容器查询自2023年起获得主流浏览器全面支持(Chrome 105+、Safari 16+、Firefox 110+)。对于需要支持旧浏览器的项目,可通过CSS.supports做特性检测,对不支持容器查询的浏览器回退到媒体查询方案。
/* 回退方案:先用媒体查询定义基础响应式 */
@media (min-width: 768px) {
.card { display: flex; flex-direction: row; }
}
/* 渐进增强:支持容器查询时覆盖为容器查询逻辑 */
@supports (container-type: inline-size) {
.card-container { container-type: inline-size; }
@container (min-width: 400px) {
.card { display: flex; flex-direction: row; }
}
@container (max-width: 399px) {
.card { display: flex; flex-direction: column; }
}
}
对于必须支持IE或旧版浏览器的场景,可以使用container-query-polyfill(PostCSS插件或JS polyfill)。JS方案通过ResizeObserver监听容器尺寸变化,动态注入样式class,但性能开销较大,仅在兼容性硬性要求时使用。
在实际项目中,建议将容器查询作为默认方案,媒体查询作为全局布局的补充。页面级布局(如侧边栏显隐、主内容区宽度)用媒体查询基于视口判断,组件级布局用容器查询基于容器判断,两者分工明确,避免混用导致维护困难。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/css-rong-qi-cha-xun-shi-zhan-xiang-ying-shi-bu-ju-cong-mei/