引言

自CSS诞生以来,响应式设计一直依赖媒体查询(Media Queries)来实现。媒体查询根据视口(viewport)的尺寸来调整样式,这在过去十几年中是响应式设计的基石。然而,随着组件化开发的普及,媒体查询的局限性日益明显:一个组件的样式应该取决于它所在的容器大小,而非整个视口的大小。

CSS容器查询(Container Queries)的出现彻底改变了这一局面。到2026年,容器查询已经获得了所有主流浏览器的全面支持,是时候深入掌握这项革命性的CSS特性了。

一、为什么需要容器查询

1.1 媒体查询的困境

考虑一个常见的场景:你的页面中有一个卡片组件,这个卡片可能出现在侧边栏(窄容器)中,也可能出现在主内容区(宽容器)中。

/* 使用媒体查询的问题 */
.card {
    display: flex;
    flex-direction: column;
    padding: 1rem;
}

/* 当视口宽度大于768px时改为横向布局 */
@media (min-width: 768px) {
    .card {
        flex-direction: row;
    }
}

问题在于:当视口宽度大于768px时,即使在窄的侧边栏中,卡片也会变成横向布局——这显然不是我们想要的效果。我们真正需要的是根据卡片自身所在容器的宽度来决定布局方式。

1.2 容器查询的优势

容器查询让组件能够”感知”自身容器的大小,从而做出更精确的布局调整:

/* 使用容器查询 - 组件根据自身容器大小调整 */
.card-container {
    container-type: inline-size;
    container-name: card;
}

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

这样,无论卡片被放在页面的哪个位置,只要它的容器宽度足够,就会自动切换为横向布局。

二、容器查询基础语法

2.1 定义容器

使用container-type属性将一个元素定义为容器:

.sidebar {
    container-type: inline-size;
    container-name: sidebar;
}

.main-content {
    container-type: inline-size;
    container-name: main;
}

container-type有三个可选值:

说明
inline-size 只追踪内联方向(宽度)的变化
size 追踪宽度和高度的变化
normal 默认值,不创建容器

性能建议:大多数情况下使用inline-size就足够了,因为高度变化触发的容器查询较少,且size类型可能影响布局性能。

2.2 编写容器查询

使用@container规则编写基于容器尺寸的样式:

@container (min-width: 400px) {
    .product-card {
        display: grid;
        grid-template-columns: 200px 1fr;
        gap: 1.5rem;
    }
}

@container (max-width: 399px) {
    .product-card {
        display: flex;
        flex-direction: column;
    }
}

2.3 命名容器

当页面中有多个容器时,使用命名容器可以精确控制:

@container sidebar (min-width: 250px) {
    .nav-item {
        flex-direction: row;
        gap: 0.5rem;
    }
}

@container card (min-width: 300px) {
    .card-content {
        columns: 2;
    }
}

三、实际应用场景

3.1 自适应卡片组件

<div class="card-wrapper">
    <div class="card">
        <img src="product.jpg" alt="产品图片" class="card-img">
        <div class="card-body">
            <h3 class="card-title">智能手表 Pro</h3>
            <p class="card-desc">搭载最新健康监测传感器</p>
            <div class="card-footer">
                <span class="price">¥2,999</span>
                <button class="btn-buy">立即购买</button>
            </div>
        </div>
    </div>
</div>
.card-wrapper {
    container-type: inline-size;
}

.card {
    display: flex;
    flex-direction: column;
    border-radius: 12px;
    overflow: hidden;
    background: white;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

/* 容器宽度大于400px时切换为横向布局 */
@container (min-width: 400px) {
    .card {
        flex-direction: row;
    }

    .card-img {
        width: 200px;
        height: auto;
        object-fit: cover;
    }

    .card-body {
        display: flex;
        flex-direction: column;
        justify-content: space-between;
    }

    .card-footer {
        display: flex;
        justify-content: space-between;
        align-items: center;
    }
}

/* 容器宽度大于600px时显示更多信息 */
@container (min-width: 600px) {
    .card-desc {
        display: block;
    }

    .card-footer {
        margin-top: auto;
    }
}

3.2 自适应导航菜单

.nav-container {
    container-type: inline-size;
}

.nav {
    display: flex;
    gap: 0.25rem;
    overflow-x: auto;
}

@container (min-width: 500px) {
    .nav {
        overflow-x: visible;
        gap: 1rem;
    }

    .nav-item {
        padding: 0.5rem 1rem;
    }
}

@container (min-width: 800px) {
    .nav {
        gap: 2rem;
    }

    .nav-item::after {
        content: '';
        position: absolute;
        bottom: 0;
        left: 0;
        width: 0;
        height: 2px;
        transition: width 0.3s;
    }

    .nav-item:hover::after {
        width: 100%;
    }
}

3.3 响应式表单布局

.form-container {
    container-type: inline-size;
}

.form-group {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    margin-bottom: 1rem;
}

@container (min-width: 500px) {
    .form-row {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 1rem;
    }
}

@container (min-width: 700px) {
    .form-row {
        grid-template-columns: 1fr 1fr 1fr;
    }

    .form-actions {
        display: flex;
        justify-content: flex-end;
        gap: 1rem;
    }
}

四、容器查询单位

CSS容器查询还引入了新的长度单位,这些单位相对于容器的尺寸:

单位 说明
cqw 容器宽度的1%
cqh 容器高度的1%
cqi 容器内联尺寸的1%
cqb 容器块尺寸的1%
cqmin cqi和cqb中的较小值
cqmax cqi和cqb中的较大值
.card-wrapper {
    container-type: inline-size;
}

.card-title {
    /* 字体大小随容器宽度缩放 */
    font-size: clamp(1rem, 3cqi, 2rem);
}

.card-img {
    /* 图片宽度始终是容器的30% */
    width: 30cqi;
}

五、从媒体查询迁移

5.1 迁移策略

迁移到容器查询不需要一步到位,可以采用渐进式策略:

  1. 识别组件边界:找出那些应该根据自身容器而非视口调整的组件
  2. 添加容器定义:为这些组件的父元素添加container-type
  3. 替换查询规则:将相关的媒体查询替换为容器查询
  4. 测试验证:在不同布局和视口大小下验证效果

5.2 常见迁移模式

/* 迁移前:基于视口的响应式 */
@media (min-width: 768px) {
    .widget {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (min-width: 1200px) {
    .widget {
        grid-template-columns: repeat(3, 1fr);
    }
}

/* 迁移后:基于容器的响应式 */
.widget-wrapper {
    container-type: inline-size;
}

@container (min-width: 500px) {
    .widget {
        grid-template-columns: repeat(2, 1fr);
    }
}

@container (min-width: 800px) {
    .widget {
        grid-template-columns: repeat(3, 1fr);
    }
}

六、浏览器支持与兼容方案

到2026年,所有主流浏览器都已全面支持容器查询:

对于需要支持旧浏览器的项目,可以使用@supports进行渐进增强:

/* 基础样式(所有浏览器) */
.card {
    display: flex;
    flex-direction: column;
}

/* 容器查询增强(支持的浏览器) */
@supports (container-type: inline-size) {
    .card-wrapper {
        container-type: inline-size;
    }

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

/* 媒体查询回退(不支持的浏览器) */
@supports not (container-type: inline-size) {
    @media (min-width: 768px) {
        .card {
            flex-direction: row;
        }
    }
}

结语

CSS容器查询代表了响应式设计的一次根本性进化。它将响应式的关注点从”视口”转移到了”组件”,使得真正的组件化响应式设计成为可能。随着浏览器支持的全面成熟,现在是学习和采用容器查询的最佳时机。

在未来的前端开发中,容器查询将成为组件库和设计系统的标配特性。掌握容器查询,不仅能提升你的CSS技能,更能帮助你构建出更灵活、更可复用、更优雅的组件系统。


💡 推荐工具:正在寻找高质量的开发工具和模板?看看这些: