CSS 布局在过去几年经历了翻天覆地的变化。Flexbox 和 Grid 的普及让我们终于可以告别各种 hack 式的布局方案。本文从实际项目出发,深入梳理一套可复用的现代 CSS 布局体系,涵盖 Flexbox、Grid、Container Queries 和现代 CSS 实用技巧。

一、Flexbox:一维布局的终极方案

Flexbox 解决了一维布局中几乎所有痛点:垂直居中、等分布局、剩余空间分配。它的核心在于主轴(main axis)和交叉轴(cross axis)的概念。

/* 经典垂直居中 —— 一行代码解决 */
.center {
  display: flex;
  align-items: center;
  justify-content: center;
}

/* 导航栏:logo 左对齐,菜单右对齐 */
.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

/* 等分布局:gap 替代 margin 负值 */
.flex-equal {
  display: flex;
  gap: 16px;
}
.flex-equal > * {
  flex: 1;
}

gap 属性是近年来 Flexbox 最实用的增强。回顾过去,我们不得不用 margin + :last-child 清除来模拟间距,繁琐且脆弱。现在一个 gap: 16px 就能搞定。

flex: 1flex-grow: 1; flex-shrink: 1; flex-basis: 0 的缩写,它让所有子项等分剩余空间。而 flex: auto 则是 flex-basis: auto,子项会根据自身内容大小分配空间——理解这个细节能让你精准控制布局行为。

实战经验:Flexbox 用得好,90% 的页面布局问题都能解决。遇到复杂二维布局再考虑 Grid。

二、CSS Grid:二维布局的利器

Grid 是真正的二维布局系统。它不关心 DOM 顺序,只关心你在网格中如何放置元素。这是它与 Flexbox 的根本区别。

/* 经典圣杯布局:header / main+sidebar / footer */
.layout {
  display: grid;
  grid-template-columns: 1fr 300px;
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header  header"
    "content sidebar"
    "footer  footer";
  min-height: 100vh;
  gap: 24px;
}

.layout header  { grid-area: header; }
.layout main    { grid-area: content; }
.layout aside   { grid-area: sidebar; }
.layout footer  { grid-area: footer; }

grid-template-areas 让布局意图一目了然。它比嵌套多个 Flexbox 容器更直观,修改布局时只需调整 ASCII 区域图,无需改动 HTML 结构。

2.1 隐式网格与自动排列

当网格项目数量不确定时,隐式网格发挥作用。配合 auto-fillauto-fit,可以实现真正弹性的卡片网格:

.card-grid {
  display: grid;
  /* auto-fill:创建尽可能多的轨道,哪怕空着 */
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 20px;
}

/* auto-fit 与 auto-fill 的区别 */
/* auto-fill 保留空轨道,auto-fit 折叠空轨道 */
.grid-demo {
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}

auto-fillauto-fit 的差别很微妙:当一行放不下更多 280px 的列时,auto-fill 保留空轨道(占用空间),auto-fit 折叠空轨道(让已有项目扩展)。多数场景推荐 auto-fit

三、现代 CSS 布局新特性

3.1 Container Queries

Container Queries 是响应式设计的新范式。它让组件根据自身容器宽度(而非视口宽度)来调整样式,彻底解决了组件复用时的样式耦合问题。

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

/* 容器查询 */
@container sidebar (max-width: 400px) {
  .widget-header {
    flex-direction: column;
  }
  .widget-title {
    font-size: 1rem;
  }
}

/* 简写 —— 直接用 inline-size 作为容器 */
.card-container {
  container: card / inline-size;
}

@container card (min-width: 500px) {
  .card {
    display: grid;
    grid-template-columns: 200px 1fr;
  }
}

实际案例:一个产品卡片组件,在侧边栏(窄)中是上下布局,在主内容区(宽)中是左右布局。用 Container Queries 一行媒体查询都不用写。

3.2 逻辑属性

逻辑属性让布局真正适配各种书写模式。如果你的站点需要支持阿拉伯语(从右往左)或竖排文字,逻辑属性不可或缺:

/* 物理属性 —— 不灵活 */
.element {
  margin-left: 16px;
  border-right: 2px solid;
}

/* 逻辑属性 —— 自动适应书写方向 */
.element {
  margin-inline-start: 16px;
  border-inline-end: 2px solid;
  padding-block: 12px;       /* padding-top + padding-bottom */
  inset-inline: 20px;        /* left + right */
}

推荐从现在开始在新项目中使用 margin-inlinepadding-block 等逻辑属性。它们在现代浏览器中已经得到全面支持,且语义更清晰。

3.3 Subgrid 实战

Subgrid 让嵌套 Grid 容器继承外层网格线,解决了多层 Grid 嵌套时对齐不一致的问题:

.parent {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: 20px;
}

.child {
  grid-column: span 3;
  display: grid;
  grid-template-columns: subgrid;
  /* 子网格继承父级的 6 列网格线,自动适配 */
}

四、布局决策框架

这是我在项目中总结的布局方案选择流程:

布局需求
  ├── 一维排列(行或列)
  │   ├── 需要换行 → flex-wrap + gap
  │   └── 不换行   → display: flex + flex: n
  ├── 二维排列(行+列)
  │   ├── 固定区域  → grid-template-areas
  │   ├── 未知数量  → repeat(auto-fit, minmax(...))
  │   └── 精确控制  → grid-template-columns + grid-row
  └── 响应式组件
      └── 依赖容器宽度 → container queries

五、实战:博客卡片组件

结合以上知识点,实现一个同时兼容 Flexbox 和 Grid 的博客卡片组件:

.blog-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 24px;
}

.blog-card {
  display: flex;
  flex-direction: column;
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 1px 3px rgba(0,0,0,0.1);
  transition: transform 0.2s, box-shadow 0.2s;
}

.blog-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 8px 25px rgba(0,0,0,0.12);
}

.blog-card-content {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: 20px;
}

.blog-card .read-more {
  margin-top: auto; /* 将按钮推到底部 */
}

@container blog (max-width: 400px) {
  .blog-card {
    flex-direction: row; /* 窄容器下行内排列 */
  }
}

这里 Grid 负责外层布局(自动适应列数),Flexbox 负责卡片内部结构(上中下排列),Container Queries 处理窄容器下的自适应——各司其职。

六、性能与最佳实践

  • 优先使用 Gapgapmargin 更简洁,且不会影响子元素的点击区域
  • 避免过度嵌套:能用 Grid 区域解决的布局,不要叠三层 Flexbox
  • 使用 min-height: 100dvhdvh(dynamic viewport height)解决了移动端地址栏伸缩导致的布局跳动
  • Grid 优先于 JS 布局:能用 CSS 实现的瀑布流(masonry)布局,不要引入 jQuery Masonry
  • 善用 :has():父选择器结合 Grid 可以实现根据子元素数量调整布局的魔幻效果
/* 示例:根据子元素数量调整列数 */
.container:has(> :last-child:nth-child(2)) {
  grid-template-columns: 1fr 1fr;
}
.container:has(> :last-child:nth-child(3)) {
  grid-template-columns: 1fr 1fr 1fr;
}

七、总结

现代 CSS 布局已经形成了一套完整的体系:Flexbox 解决一维排列,Grid 驾驭二维结构,Container Queries 实现组件级响应式,逻辑属性保证国际化兼容。这四者结合,足以应对 99% 的页面布局需求。

关键是要理解每个工具的核心场景,而不是在所有地方都用一个方案。实际项目中 Flexbox 和 Grid 的混用频率很高——Flexbox 用于组件内部,Grid 用于页面骨架,这是最成熟的分工模式。

下一篇我将深入 CSS Container Queries 的高级用法和真实项目迁移案例,敬请关注。