CSS Container Queries 实战:告别媒体查询,真正让组件自适应

CSS Container Queries 实战:告别媒体查询,真正让组件自适应

做前端的都知道,写响应式布局这么多年,我们一直在用 @media(媒体查询)来调样式。但你有没有遇到过这种情况:一个卡片组件在侧边栏里要竖排,到了主内容区就得横排,然后你就在那疯狂写覆盖样式,最后搞得 CSS 又臭又长。

这就是老方案的硬伤——媒体查询只能看视口大小,根本不关心你组件的爹是谁。

所以我今天要聊的,就是一个能从根本上解决这个问题的方案:CSS Container Queries,容器查询。

它到底好在哪

先看对比数据。传统的媒体查询写法,大概是这样的:

/* 基于视口宽度的媒体查询 */
@media (min-width: 768px) {
  .card { display: flex; flex-direction: row; }
}
@media (max-width: 767px) {
  .card { display: flex; flex-direction: column; }
}

看着没啥问题对吧?但是,这个 .card 组件如果放在 300px 宽的侧边栏里,768px 的视口下它其实只有很小的地方,但媒体查询还是会把它变成横排。你想让它自适应,就得多写一套 .sidebar .card 的覆盖样式。

用了 Container Queries 之后:

/* 定义容器 */
.card-wrapper {
  container-type: inline-size;
  container-name: card;
}

/* 根据容器宽度自适应 */
@container card (min-width: 400px) {
  .card { display: flex; flex-direction: row; }
}
@container card (max-width: 399px) {
  .card { display: flex; flex-direction: column; }
}

同一个 .card,不管放在侧边栏还是主内容区,自动根据父容器的实际宽度来切换布局。这就是真正的组件级响应式。

三个核心概念,一篇文章讲透

要上手 Container Queries,你只需要搞明白三个东西:container-type、container-name 和 @container 规则。

container-type:谁来做容器

这个是声明”我要把我的某个元素变成容器”。有三个值:

.container {
  /* 只关注内联方向(一般是宽度),最常用 */
  container-type: inline-size;

  /* 宽度和高度都关注,但会触发 contain,慎用 */
  /* container-type: size; */

  /* 不做容器查询, 默认值 */
  /* container-type: normal; */
}

日常 90% 的场景只用 inline-size 就够了。size 不仅要管高度,还会强制开启 contain,有时会引发布局异常,非特殊需求别用。

container-name:给容器起个名

如果你的页面里有很多容器,嵌套着用,就需要给它们各起个名,不然查询时就懵了。

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

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

简写语法更清爽:

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

@container:真正发力的查询

语法和 @media 几乎一样,但它查的是容器尺寸。

@container card (min-width: 500px) {
  .card-title { font-size: 1.5rem; }
  .card-body { display: grid; grid-template-columns: 1fr 1fr; }
}

@container card (max-width: 499px) {
  .card-title { font-size: 1rem; }
  .card-body { display: flex; flex-direction: column; }
}

注意!这里的 min-width 是指容器 .card-container 的宽度,不是视口宽度。这个区别就是整个 Container Queries 的灵魂。

浏览器兼容性(2026 年现状)

截止 2026 年中,Container Queries 已经得到所有主流浏览器的全面支持:

浏览器 支持版本
Chrome 105+ ✅ 完全支持
Firefox 110+ ✅ 完全支持
Safari 16+ ✅ 完全支持
Edge 105+ ✅ 完全支持

所以别担心兼容性了,直接干就行。

实战一:响应式卡片组件

这是最经典的场景。一个卡片在宽容器里横排,窄容器里竖排。

/* 定义容器 */
.card-container {
  container: card-item / inline-size;
}

/* 卡片基础样式 */
.card-item {
  display: flex;
  flex-direction: column;  /* 默认竖排 */
  gap: 1rem;
  padding: 1rem;
  border-radius: 12px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}

.card-image {
  width: 100%;
  border-radius: 8px;
}

/* 容器宽度 >= 400px 时变横排 */
@container card-item (min-width: 400px) {
  .card-item {
    flex-direction: row;
    align-items: center;
  }
  .card-image {
    width: 200px;
    flex-shrink: 0;
  }
}

/* 容器宽度 >= 700px 时更大气的排版 */
@container card-item (min-width: 700px) {
  .card-item {
    padding: 2rem;
  }
  .card-title { font-size: 1.75rem; }
  .card-image { width: 300px; }
}

你看,一个 .card-item,放在 300px 宽的侧边栏、500px 宽的内容区、800px 宽的页面主体里,分别展示不同的布局——完全自动,不需要额外写任何覆盖样式。

实战二:Dashboard 自适应网格

Dashboard 是最适合 Container Queries 的场景之一。不同宽度的 widget 块自动调整内部布局。

.widget {
  container: widget-box / inline-size;
  border-radius: 16px;
  background: #fff;
  padding: 1rem;
}

/* 小 widget:紧凑 */
@container widget-box (max-width: 300px) {
  .widget-header { font-size: 0.875rem; }
  .widget-chart { height: 120px; }
  .widget-data { display: none; }
}

/* 中 widget:标准 */
@container widget-box (min-width: 301px) and (max-width: 600px) {
  .widget-header { font-size: 1rem; }
  .widget-chart { height: 200px; }
}

/* 大 widget:完整版 */
@container widget-box (min-width: 601px) {
  .widget-header { 
    font-size: 1.25rem; 
    display: flex; 
    justify-content: space-between; 
  }
  .widget-chart { height: 300px; }
  .widget-data { display: grid; grid-template-columns: repeat(3, 1fr); }
}

这个模式在 Grafana、Datadog 那种监控面板里特别好使。一个组件三套样式,完全自动切换。

实战三:结合 CSS 容器查询单位

Container Queries 还带来了一组新单位,用起来贼爽:

单位 含义
cqw 容器宽度的 1%
cqh 容器高度的 1%
cqi 容器内联方向尺寸的 1%
cqmin cqi 和 cqb 中较小的那个
cqmax cqi 和 cqb 中较大的那个
.widget-content {
  container: widget-body / inline-size;
}

@container widget-body (min-width: 400px) {
  .metric-value {
    /* 字体大小随容器宽度自适应 */
    font-size: clamp(1.5rem, 5cqi, 3rem);
  }
  .metric-unit {
    font-size: 0.6em;
    margin-left: 0.25em;
  }
}

这里的 5cqi 就是容器宽度的 5%,配合 clamp() 既能自适应,又限制了大小范围,简直完美。

进阶:嵌套容器查询

Container Queries 可以嵌套使用,每个层级的容器只查自己的父容器。

/* 外层容器 */
.page-section {
  container: section / inline-size;
}

/* 内层卡片容器 */
.card-wrapper {
  container: card-inner / inline-size;
}

/* 根据 section 宽度调整卡片列数 */
@container section (min-width: 900px) {
  .card-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}

@container section (max-width: 899px) {
  .card-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* 根据卡片自身容器宽度调整卡片内部布局 */
@container card-inner (min-width: 350px) {
  .card-item { flex-direction: row; }
}

这样,页面布局层级的响应式交给外层容器管,组件内部的响应式交给内层容器管,各司其职。

和媒体查询怎么分工?

Container Queries 虽然强,但不是来取代 @media 的。它俩有明确的分工:

媒体查询负责全局布局——几行网格、页面上下间距、菜单是否折叠,这些由视口决定的事,还是 @media 干。

容器查询负责组件自适应——卡片、面板、表格、列表等组件根据容器空间自动调整,这才是 Container Queries 的战场。

我现在的习惯写法是:

/* 全局布局:用媒体查询 */
@media (min-width: 1200px) {
  .layout-main { grid-template-columns: 280px 1fr; }
}
@media (max-width: 1199px) {
  .layout-main { grid-template-columns: 1fr; }
}

/* 组件内部:用容器查询 */
.widget-container {
  container: widget / inline-size;
}
@container widget (min-width: 500px) {
  /* 组件自适应的样式 */
}

一个管页面的骨架层级,一个管组件的血肉层级,分工清楚,各干各的。

踩过的几个坑

用了半年多,也踩了不少坑,给你提个醒:

1. 容器本身不要用 @container 改布局。 容器的尺寸变化不能被自身的 @container 感知,这会导致死循环。容器的样式改用媒体查询控制。

2. container-type: size 要慎用。 它需要用 contain: layout style size,可能会导致绝对定位的子元素错位。没特殊需要时,一律用 inline-size。

3. Firefox 旧版本的 contain 问题。 114 之前的版本会遇到 contain 和 Canvas 2D 的冲突。不过现在最新版已经修了。

4. DevTools 调试。 Chrome DevTools 已经支持了,Elements 面板里找到容器元素,会显示一个 container 标签,点它就能看到所有 @container 规则和当前命中的断点状态。

写在最后

Container Queries 是我 2026 年最推荐的 CSS 新特性之一。如果说 CSS Grid 解决了二维布局的问题,那 Container Queries 解决的就是”组件在哪里都能活得很好”的问题。

以前写一个组件要考虑它在页面的每个位置长什么样,现在只管定好容器,组件自己会适配。这种”组件自治”的思路,跟组件化开发的理念天然吻合。

现在浏览器全面支持了,真没什么理由不上了。找个项目里的卡片或者面板,试试换 Container Queries 重写,你就知道有多爽。

评论区

0 条评论

登录后可评论。

查看完整榜单
查看完整榜单
查看完整榜单