聊天讨论 CSS Container Queries:彻底告别 @media 写到手软,附 5 个真实布局案例

193577746(kyriewen) · June 10, 2026 · 13 hits

以前写响应式组件,总要根据视口宽度写一堆 @media,代码又臭又长。现在 Container Queries 让组件根据父容器尺寸响应,代码直接减半。本文讲透语法 + 5 个可直接复用的案例。


一、Container Queries 解决了什么问题?

@media 媒体查询基于视口宽度。这导致一个组件在不同父容器下无法独立响应——同样的卡片,在侧边栏和主内容区需要两套样式或者额外类名。

Container Queries 允许你定义:当父容器达到某个宽度时,子元素改变布局。

/* 以前:基于视口,无法区分卡片在哪个容器里 */
@media (min-width: 600px) {
  .card { display: flex; }
}

/* 现在:基于父容器宽度 */
@container (min-width: 300px) {
  .card { display: flex; }
}

二、基础语法(三步搞定)

2.1 定义容器

.sidebar {
  container-type: inline-size;   /* 监听内联方向(宽度)变化 */
  container-name: sidebar;       /* 可选,给容器起名 */
}
  • container-type: inline-size 最常用,表示根据宽度变化。
  • 也可以 size(宽高都监听)或 normal(不监听)。

2.2 使用 @container

@container (min-width: 300px) {
  .card-title {
    font-size: 1.5rem;
  }
}

如果只有一个容器,可以直接写条件。如果有多个容器,用 container-name 限定:

@container sidebar (min-width: 300px) {
  /* 只对 sidebar 容器内的元素生效 */
}

2.3 组合与范围

@container (100px <= width < 300px) {
  /* 容器宽度在 100px 到 300px 之间 */
}

三、与 @media 对比(代码量减少 50%)

场景 传统 @media 做法 Container Queries 做法
卡片在侧边栏时垂直,在主区域时水平 写两套类名或全局判断 父容器定义后,卡片内一套规则自动适配
仪表盘小部件适应不同格子尺寸 根据视口断点统一控制 每个小部件独立响应自己的容器

代码对比

<!-- 以前:需要额外类名 -->
<div class="sidebar">
  <div class="card vertical"></div>
</div>
<div class="main">
  <div class="card horizontal"></div>
</div>
/* 以前:手动控制 */
.card.vertical { flex-direction: column; }
.card.horizontal { flex-direction: row; }

/* Container Queries:一套规则 */
.card {
  container-type: inline-size;
}
@container (min-width: 400px) {
  .card { flex-direction: row; }
}
@container (max-width: 399px) {
  .card { flex-direction: column; }
}

四、5 个实战案例(可直接复制)

案例 1:卡片组件根据容器宽度切换布局

<div class="card-container">
  <div class="card">
    <img src="photo.jpg" alt="">
    <div class="card-body">
      <h3>标题</h3>
      <p>描述文字</p>
    </div>
  </div>
</div>
.card-container {
  container-type: inline-size;
}
.card {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
@container (min-width: 350px) {
  .card {
    flex-direction: row;
    align-items: center;
  }
  .card img {
    width: 40%;
  }
}

案例 2:侧边栏与主内容区共享同一卡片组件

/* 不需要任何额外代码,卡片自己响应父容器宽度 */
.sidebar { container-type: inline-size; width: 280px; }
.main { container-type: inline-size; width: 800px; }
/* 卡片规则如上,当父容器<350px时垂直,>350px时水平 */

案例 3:仪表盘小部件矩阵

<div class="dashboard">
  <div class="widget" style="width: 200px">...</div>
  <div class="widget" style="width: 400px">...</div>
  <div class="widget" style="width: 200px">...</div>
</div>
.widget {
  container-type: inline-size;
}
.widget-content {
  font-size: 12px;
}
@container (min-width: 250px) {
  .widget-content {
    font-size: 16px;
    display: flex;
    gap: 1rem;
  }
}

案例 4:商品列表切换行列(类似 Grid 与 Flex 混合)

.product-list {
  container-type: inline-size;
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}
.product-item {
  flex: 1 1 200px;
}
@container (max-width: 500px) {
  .product-item {
    flex-basis: 100%;  /* 小于500px时每行一个 */
  }
}

案例 5:自适应导航栏(折叠菜单)

<nav class="navbar">
  <div class="logo">Logo</div>
  <ul class="nav-links">
    <li>首页</li>
    <li>产品</li>
    <li>关于</li>
  </ul>
</nav>
.navbar {
  container-type: inline-size;
  display: flex;
  justify-content: space-between;
}
.nav-links {
  display: flex;
  gap: 1rem;
}
@container (max-width: 500px) {
  .navbar {
    flex-direction: column;
  }
  .nav-links {
    flex-direction: column;
    margin-top: 0.5rem;
  }
}

五、浏览器兼容性与降级方案

支持情况(截至 2026-06):

  • Chrome 105+ ✅
  • Firefox 110+ ✅
  • Safari 16+ ✅
  • Edge 105+ ✅

如果需要兼容旧浏览器,使用 @supports 渐进增强:

/* 降级:默认样式(例如始终垂直) */
.card {
  flex-direction: column;
}
/* 支持 Container Queries 时覆盖 */
@supports (container-type: inline-size) {
  .card-container {
    container-type: inline-size;
  }
  @container (min-width: 350px) {
    .card {
      flex-direction: row;
    }
  }
}

六、总结

  • Container Queries 让组件根据父容器而非视口响应,实现真正的组件级响应式。
  • 语法简单:container-type + @container
  • 5 个案例覆盖卡片、侧边栏、仪表盘、商品列表、导航栏。
  • 兼容性已可投入生产,配合 @supports 安全降级。

文中所有代码均可直接复制到项目中。以后写响应式组件,先问自己:这个样式是由视口决定,还是由父容器决定?

讨论:你在开发中遇到过哪些因 @media 全局断点导致的组件复用困难?欢迎留言分享。

No Reply at the moment.
You need to Sign in before reply, if you don't have an account, please Sign up first.