返回博客

CSS Container Queries 实战

传统的媒体查询(Media Queries)基于视口宽度调整布局。但现代 Web 应用中,组件经常被放置在不同宽度的容器中——侧边栏、弹窗、网格列。Container Queries 让组件能够根据父容器而非视口的尺寸来调整样式,这是响应式设计的重大进化。

核心价值:组件可以在任何容器中自适应,无需知道它将被放在页面的哪个位置。

一、为什么需要 Container Queries

考虑一个卡片组件,它可能出现在:

使用媒体查询,你无法知道卡片在哪个容器中,只能基于视口宽度猜测:

/* 媒体查询:基于视口 */
@media (min-width: 768px) {
  .card {
    display: flex; /* 视口 ≥768px 时横向布局 */
  }
}

/* 问题:侧边栏中卡片宽度只有 300px,但视口是 1440px */
/* 结果:卡片被挤压,布局不合理 */

Container Queries 解决了这个问题:

/* 容器查询:基于父容器 */
@container (min-width: 400px) {
  .card {
    display: flex; /* 容器 ≥400px 时横向布局 */
  }
}

/* 卡片根据自身所在容器调整布局 */
/* 侧边栏中:容器 300px → 垂直布局 */
/* 主内容区:容器 800px → 水平布局 */

二、基础用法

1. 定义容器

/* 父元素声明为容器 */
.card-container {
  container-type: inline-size;
  /* 或简写 */
  container: card / inline-size;
}

/* container-type 可选值:
 * - inline-size: 查询行向尺寸(宽度)
 * - size: 查询双向尺寸(宽度和高度)
 * - normal: 默认值,不是查询容器
 */

2. 编写容器查询

/* 默认样式:窄容器 */
.card {
  display: block;
}

.card-image {
  width: 100%;
  height: 200px;
}

/* 容器宽度 ≥400px */
@container (min-width: 400px) {
  .card {
    display: flex;
    gap: 1rem;
  }
  
  .card-image {
    width: 200px;
    flex-shrink: 0;
  }
}

/* 容器宽度 ≥600px */
@container (min-width: 600px) {
  .card {
    padding: 2rem;
  }
  
  .card-title {
    font-size: 1.5rem;
  }
}

3. 命名容器

当有多个嵌套容器时,可以指定查询哪个容器:

/* 定义命名容器 */
.sidebar {
  container: sidebar / inline-size;
}

.main-content {
  container: main / inline-size;
}

/* 指定查询特定容器 */
@container sidebar (min-width: 300px) {
  .widget {
    /* 只响应 sidebar 容器的宽度 */
  }
}

@container main (min-width: 600px) {
  .article {
    /* 只响应 main 容器的宽度 */
  }
}

三、实战案例

案例 1:自适应卡片组件

<!-- HTML -->
<div class="layout">
  <aside class="sidebar">
    <div class="card-container">
      <article class="card">
        <img src="photo.jpg" alt="" class="card-img">
        <div class="card-content">
          <h3 class="card-title">标题</h3>
          <p class="card-desc">描述文字</p>
        </div>
      </article>
    </div>
  </aside>
  
  <main class="content">
    <div class="card-container">
      <article class="card">...</article>
    </div>
  </main>
</div>
/* CSS */
.card-container {
  container-type: inline-size;
}

.card {
  display: flex;
  flex-direction: column;
  border-radius: 8px;
  overflow: hidden;
}

.card-img {
  width: 100%;
  height: 180px;
  object-fit: cover;
}

.card-content {
  padding: 1rem;
}

/* 容器 ≥350px:水平布局 */
@container (min-width: 350px) {
  .card {
    flex-direction: row;
  }
  
  .card-img {
    width: 140px;
    height: 100%;
  }
}

/* 容器 ≥500px:更大的图片 */
@container (min-width: 500px) {
  .card-img {
    width: 200px;
  }
  
  .card-content {
    padding: 1.5rem;
  }
}

案例 2:响应式导航

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

.nav {
  display: flex;
  gap: 1rem;
}

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

/* 窄容器:垂直导航 */
@container (max-width: 500px) {
  .nav {
    flex-direction: column;
  }
  
  .nav-link {
    width: 100%;
    text-align: center;
  }
}

/* 宽容器:水平导航 + 更多间距 */
@container (min-width: 800px) {
  .nav {
    gap: 2rem;
  }
}

案例 3:网格自适应

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

.grid {
  display: grid;
  gap: 1rem;
  grid-template-columns: 1fr;
}

/* 容器 ≥400px:两列 */
@container (min-width: 400px) {
  .grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* 容器 ≥600px:三列 */
@container (min-width: 600px) {
  .grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

/* 容器 ≥900px:四列 */
@container (min-width: 900px) {
  .grid {
    grid-template-columns: repeat(4, 1fr);
    gap: 1.5rem;
  }
}

四、容器查询单位

CSS 提供了基于容器的相对单位:

单位 含义
cqw 容器宽度的 1%
cqh 容器高度的 1%
cqi 容器行向尺寸的 1%
cqb 容器块向尺寸的 1%
cqmin cqi 和 cqb 中较小的值
cqmax cqi 和 cqb 中较大的值
/* 使用容器单位 */
.card-title {
  font-size: 5cqw; /* 标题大小为容器宽度的 5% */
}

.card-padding {
  padding: 2cqw; /* 内边距随容器缩放 */
}

.card-gap {
  gap: min(2cqw, 20px); /* 最大 20px,否则按比例 */
}

五、与媒体查询配合

Container Queries 和 Media Queries 可以组合使用:

/* 视口 ≥768px 且 容器 ≥400px */
@media (min-width: 768px) {
  @container (min-width: 400px) {
    .card {
      display: flex;
    }
  }
}

/* 或反过来 */
@container (min-width: 400px) {
  @media (prefers-color-scheme: dark) {
    .card {
      background: #1a1a1a;
    }
  }
}

六、浏览器支持与降级

Container Queries 已被所有现代浏览器支持(2023 年起)。对于不支持的浏览器:

/* 渐进增强:先写默认样式 */
.card {
  display: block; /* 降级方案 */
}

/* 现代浏览器使用容器查询 */
@supports (container-type: inline-size) {
  .card-container {
    container-type: inline-size;
  }
  
  @container (min-width: 400px) {
    .card {
      display: flex;
    }
  }
}
性能提示:容器查询会增加浏览器的计算开销。避免在深层嵌套中过度使用,只在真正需要的组件上定义容器。

总结

Container Queries 是响应式设计的重要进化: