Skip to content

CSS Container Queries 浏览器支持在即

关注 CSS 规范的同学可能已经注意到了,Container Queries 已经进入 Chrome Canary 实验阶段。这个特性等了好几年,终于要来了。

什么是 Container Queries

Media Queries 根据视口宽度响应,Container Queries 根据父容器宽度响应。

这才是组件化开发真正需要的能力。

css
/* Media Queries:根据浏览器窗口宽度 */
@media (min-width: 768px) {
  .card { flex-direction: row; }
}

/* Container Queries:根据父容器宽度 */
.card-container {
  container-type: inline-size;
}

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

为什么这个特性很重要

现在的痛点:同一个组件放在侧边栏(窄)和主内容区(宽),表现应该不同。但 Media Queries 只看视口,不看容器。

html
<!-- 同一个 Card 组件 -->
<div class="sidebar">
  <Card /> <!-- 侧边栏窄,应该显示紧凑布局 -->
</div>

<main>
  <Card /> <!-- 主内容区宽,应该显示完整布局 -->
</main>

<!-- 两个 Card 在同一个视口下,Media Queries 无法区分 -->

语法详解

css
/* 1. 定义容器 */
.card-container {
  container-type: inline-size;  /* 只监控 inline 方向(水平)的尺寸 */
  container-name: card;          /* 可选:给容器命名 */
}

/* 简写 */
.card-container {
  container: card / inline-size;
}

/* 2. 使用 @container 查询 */
@container card (min-width: 400px) {
  .card {
    display: grid;
    grid-template-columns: 200px 1fr;
  }

  .card__image {
    aspect-ratio: 1;
  }
}

@container card (min-width: 600px) {
  .card {
    grid-template-columns: 300px 1fr;
    gap: 2rem;
  }
}

实际案例:响应式卡片

css
.card-wrapper {
  container: card / inline-size;
}

/* 窄容器:垂直堆叠 */
.card {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.card__title {
  font-size: 1rem;
}

/* 中等容器:水平排列 */
@container card (min-width: 350px) {
  .card {
    flex-direction: row;
    align-items: center;
  }

  .card__image {
    width: 120px;
    flex-shrink: 0;
  }

  .card__title {
    font-size: 1.125rem;
  }
}

/* 宽容器:更大的布局 */
@container card (min-width: 500px) {
  .card {
    gap: 1.5rem;
  }

  .card__image {
    width: 200px;
  }

  .card__title {
    font-size: 1.25rem;
  }
}

和现有方案对比

javascript
// 现在的常见做法:用 ResizeObserver 监控容器宽度
// 不优雅,而且有性能开销

const observer = new ResizeObserver((entries) => {
  for (const entry of entries) {
    const width = entry.contentRect.width
    entry.target.classList.toggle('compact', width < 400)
    entry.target.classList.toggle('normal', width >= 400 && width < 600)
    entry.target.classList.toggle('wide', width >= 600)
  }
})

document.querySelectorAll('.card-wrapper').forEach((el) => {
  observer.observe(el)
})

// Container Queries 一行 CSS 搞定,浏览器原生优化

配合 Tailwind CSS

Tailwind 3.0 已经实验性支持 @container

html
<!-- 需要在容器上加 container 类 -->
<div class="@container">
  <div class="flex @md:flex-row @lg:gap-8">
    <img class="w-full @md:w-32 @lg:w-48" />
    <div class="@md:ml-4">
      <h3 class="text-sm @md:text-lg @lg:text-xl">标题</h3>
    </div>
  </div>
</div>

浏览器支持现状

截至 2021 年 8 月:

  • Chrome Canary:实验性支持(需要开启 flag)
  • Chrome 105+:预计默认支持
  • Firefox / Safari:暂无支持时间表

目前可以用 PostCSS 插件做降级:

bash
npm install -D @csstools/postcss-container-queries

小结

  • Container Queries 让组件根据父容器宽度响应,解决了 Media Queries 的根本局限
  • 语法:container-type 定义容器,@container 查询
  • 组件化开发的必备能力,Grid + Container Queries = 真正的响应式组件
  • 浏览器支持还在早期,但趋势已定,值得提前了解

MIT Licensed