CSS 常见布局方式:Flex 与 Grid
Category(分类): CSS Status: 未知
本文保留原文关于传统布局、Flex、Grid、居中、圣杯布局和双飞翼布局的主要内容,并修正过时的浏览器兼容性、代码语法、Grid/Flex 概念和居中示例。原文截图主要用于辅助理解,具体浏览器支持情况应以当前 MDN、Can I Use 和目标浏览器测试为准。

传统盒模型布局方式
传统页面布局通常会组合使用以下能力:
- 普通流(Normal Flow):元素按照文档顺序参与块布局或行内布局;
- 定位布局(Positioning):通过
position、top、right、bottom、left等属性调整元素位置; - 浮动布局(Float):通过
float让元素脱离普通流,并让行内内容环绕它。
display 并不只是“文档流布局”的开关,它还可以创建 Flex、Grid、Table、Flow-root 等不同的格式化上下文。
文档流布局
普通流是最基本的布局方式。块级盒通常沿块方向依次排列,行内盒在行盒中从左到右排列,空间不足时换行。
.block {
display: block;
}
.inline {
display: inline;
}
浮动布局
浮动最初主要用于图片和文字环绕:
.article-image {
float: left;
width: 240px;
margin: 0 1rem 0.5rem 0;
}
浮动元素脱离普通流中的块布局位置,但仍会影响周围的行内内容。现代页面整体布局通常优先使用 Flexbox 或 Grid。
定位布局
定位适合处理覆盖层、弹窗、角标和相对于容器的精确定位:
.container {
position: relative;
}
.badge {
position: absolute;
top: 0;
right: 0;
}
不要把绝对定位当作页面整体布局的默认方案,否则容易造成内容重叠、响应式适配困难和文档顺序与视觉顺序不一致。
Flex 布局

仅仅通过普通流、浮动和定位来实现复杂布局,往往需要较多技巧。Flexbox 可以更直接地处理一维布局、空间分配、对齐和换行。
什么是 Flex 布局?
Flex 是 Flexible Box 的缩写,意为弹性盒布局。它为盒状模型提供了沿主轴和交叉轴分配空间的能力。
Flexbox 规范经历过多个历史版本。现代 Chrome、Edge、Firefox 和 Safari 已经广泛支持 Flexbox;IE10/11 的实现较早且存在兼容性差异,IE 也已经停止维护。新项目通常不需要专门为 IE 添加 Flex 前缀,但维护旧项目时仍应根据目标浏览器测试。

使用 Flex 布局
将元素的 display 设置为 flex,它就成为 Flex 容器,直接子元素会成为 Flex item:
.flex-container {
display: flex;
}
.inline-flex-container {
display: inline-flex;
}
不要把 flex 和 inline-flex 写在同一个示例中,因为后面的声明会覆盖前面的声明。旧浏览器前缀也应根据项目目标单独处理。
设置 Flex 容器后,直接子元素的
float、clear和vertical-align不再按照普通流或普通行内布局的方式起作用。需要对齐 Flex item 时,应使用 Flexbox 自身的对齐属性。
Flex 的容器和轴
Flex 的核心概念是容器和轴:
- Flex container:设置了
display: flex或inline-flex的元素; - Flex item:Flex 容器的直接子元素;
- 主轴(main axis):项目排列的主要方向;
- 交叉轴(cross axis):与主轴垂直的方向;
- main start / main end:主轴的起点和终点;
- cross start / cross end:交叉轴的起点和终点;
- main size / cross size:项目在主轴和交叉轴方向占据的尺寸。
主轴默认由 flex-direction: row 决定,通常沿当前书写方向的行方向排列。交叉轴不是永远“顺时针旋转 90°”,它还会受到 direction、writing-mode 和反向排列值的影响。

Flex 容器属性
常用的 Flex 容器属性包括:
flex-direction:主轴方向;flex-wrap:是否换行;flex-flow:前两个属性的简写;justify-content:主轴上的对齐和剩余空间分配;align-items:单行在交叉轴上的项目对齐;align-content:多行 Flex line 在交叉轴上的整体对齐。
flex-direction
flex-direction 决定主轴方向:
.flex-container {
flex-direction: row; /* 默认,沿行方向排列 */
}
.column-container {
flex-direction: column; /* 沿块方向排列 */
}
可选值:
row:主轴沿行方向;row-reverse:沿行方向反向排列;column:主轴沿块方向;column-reverse:沿块方向反向排列。

flex-wrap
flex-wrap 决定 Flex item 在一条 Flex line 放不下时是否换行:
.flex-container {
flex-wrap: nowrap; /* 默认,不换行 */
}
.wrapped-container {
flex-wrap: wrap; /* 换行,第一行位于交叉轴起点 */
}
.reverse-wrapped-container {
flex-wrap: wrap-reverse; /* 换行,Flex line 反向排列 */
}

wrap-reverse 的实际视觉方向还会受到书写模式和交叉轴方向影响,不应简单固定描述为“第一行在下方”。
justify-content
justify-content 定义项目在主轴上的对齐和剩余空间分配:
.flex-container {
justify-content: flex-start; /* 起点对齐 */
}
.center-container {
justify-content: center;
}
.space-between-container {
justify-content: space-between;
}
.space-around-container {
justify-content: space-around;
}
.space-evenly-container {
justify-content: space-evenly;
}
常见值:
flex-start:靠主轴起点;flex-end:靠主轴终点;center:主轴居中;space-between:两端贴边,项目间均匀分配;space-around:每个项目两侧空间相等,边缘空间是项目间空间的一半;space-evenly:项目之间以及项目与容器边缘之间的空间都相等。

flex-flow
flex-flow 是 flex-direction 和 flex-wrap 的简写,初始值为 row nowrap:
.flex-container {
flex-flow: row wrap;
}
align-items
align-items 定义单条 Flex line 中项目在交叉轴上的对齐方式:
.flex-container {
align-items: stretch; /* 初始值,满足条件时拉伸项目 */
}
.center-items {
align-items: center;
}
.baseline-items {
align-items: baseline;
}
常见值:
flex-start:交叉轴起点;flex-end:交叉轴终点;center:交叉轴居中;baseline:按项目的基线对齐;stretch:项目交叉轴尺寸为auto且条件满足时进行拉伸。

align-content
align-content 定义多条 Flex line 在交叉轴上的整体对齐方式。单行 Flex 容器通常看不到它的效果,必须设置换行并且交叉轴上有剩余空间:
.flex-container {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
}
.center-lines {
align-content: center;
}
常见值还包括 flex-end、space-between、space-around、space-evenly 和 stretch。

Flex item 属性
Flex item 常用属性包括:
order:项目的视觉排列顺序;flex-grow:有剩余空间时的增长比例;flex-shrink:空间不足时的收缩比例;flex-basis:参与伸缩前的基准尺寸;flex:前三个属性的简写;align-self:单独覆盖某个项目的交叉轴对齐方式。
order
order 越小,项目通常越靠前,初始值为 0:
.first {
order: -1;
}
.last {
order: 2;
}
视觉顺序改变不会改变 DOM 顺序。涉及阅读顺序、键盘操作和屏幕阅读器时,应谨慎使用 order。

flex-grow
flex-grow 定义项目在有剩余空间时的增长比例,初始值为 0:
.item {
flex-grow: 1;
}
如果多个项目的 flex-grow 分别为 1 和 2,剩余空间会按比例分配,但实际结果仍会受到最小尺寸、最大尺寸和基准尺寸影响。

flex-shrink
flex-shrink 定义空间不足时项目的收缩比例,初始值为 1:
.item {
flex-shrink: 1;
}
它不要求 flex-wrap 必须是 nowrap。当 Flex 容器换行时,项目会先按照换行规则分到不同的 Flex line,再在各自的 line 内进行空间分配。

flex-basis
flex-basis 定义项目参与伸缩前的基准尺寸。主轴为行方向时,它通常对应宽度;主轴为列方向时,它通常对应高度:
.item {
flex-basis: 200px;
}
.auto-item {
flex-basis: auto;
}
auto 会参考项目的主轴尺寸属性和内容尺寸,具体计算还会受到 min-width、min-height 等因素影响。

flex
flex 是 flex-grow、flex-shrink 和 flex-basis 的简写:
.item {
flex: 1 1 200px;
}
其初始值是:
flex: 0 1 auto;
常用快捷值:
.item-auto {
flex: auto; /* 1 1 auto */
}
.item-none {
flex: none; /* 0 0 auto */
}
.item-fill {
flex: 1; /* 通常按 1 1 0% 处理 */
}

align-self
align-self 允许单个项目覆盖父容器的 align-items:
.item {
align-self: auto;
}
.center-item {
align-self: center;
}
.start-item {
align-self: flex-start;
}
auto 会使用父容器的 align-items。这个属性不会改变项目在主轴上的位置。

Grid 网格布局
Flexbox 主要解决一维布局问题:一次处理一条主轴。Grid 是二维布局系统,可以同时定义行和列,更适合页面骨架、卡片网格和复杂的二维区域布局。
Grid 与 HTML 表格布局都具有行列结构,但 Grid 通过 CSS 定义,不需要使用 <table>、<tr>、<td> 等特定 HTML 标签,也不会把数据语义伪装成表格。
Grid 可以在不改变 DOM 结构的情况下调整视觉布局,但不代表可以忽略语义、阅读顺序和可访问性。视觉重排时仍应保证源代码顺序合理。
Grid 的浏览器支持

现代 Chrome、Edge、Firefox 和 Safari 已经广泛支持 CSS Grid。原文的兼容性截图来自较早时期,只能作为历史资料;如果需要兼容 IE,Grid 支持不足,通常需要降级方案或其他布局实现。
Grid 的基本概念
网格线(Grid Line)
网格线是网格中行和列的分界线。每条轨道的两侧都有网格线,可以使用数字或自定义名称引用:

网格轨道(Grid Track)
相邻两条网格线之间的空间称为网格轨道。列轨道控制列方向尺寸,行轨道控制行方向尺寸:

网格单元格(Grid Cell)
由相邻两条行线和两条列线围成的最小区域称为网格单元格:

网格区域(Grid Area)
由任意四条网格线围成的区域称为网格区域。一个区域可以包含一个或多个网格单元格,类似表格中的合并单元格:

使用 Grid 布局
将元素的 display 设置为 grid 或 inline-grid,它就成为 Grid 容器,直接子元素会成为 Grid item:
.grid-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-template-rows: auto;
}
subgrid 不是 display 的值,而是可以用于 Grid item 的轨道定义:
.child-grid {
display: grid;
grid-template-columns: subgrid;
}
使用 subgrid 时,项目必须处于合适的父级 Grid 轨道环境中。
对于 Grid item,float、clear 和 vertical-align 不会按照普通流、浮动或行内布局的方式控制其位置,应使用 grid-area、place-items、align-self、justify-self 等 Grid 属性。
不设置 grid-template-columns
如果不设置显式列轨道,在简单的自动放置场景下,Grid item 通常会按一列排列,并生成隐式轨道:
<div class="grid-container">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
</div>
.grid-container {
display: grid;
grid-template-rows: 50px 80px 100px;
background: pink;
}
.item {
border: 2px solid palegoldenrod;
color: white;
font-size: 20px;
text-align: center;
}

如果存在显式定位或其他自动放置规则,也可能生成隐式列,不能简单地认为永远只有一列。
设置列和行
grid-template-columns 定义显式列轨道,grid-template-rows 定义显式行轨道:
.grid-container {
display: grid;
grid-template-columns: 50px 40px 100px 80px;
grid-template-rows: 50px 100px 60px 80px;
padding: 20px;
background: pink;
}
设置了 4 列和 4 行时,即使只有 6 个项目,未被项目占用的显式轨道仍然存在,只是可能没有可见内容。不能说“后面的轨道值无效”。

fr 单位
fr 是 Grid 的弹性轨道单位,用于在扣除固定尺寸轨道、间距和内边距后分配剩余空间:
.grid-container {
display: grid;
grid-template-columns: 1fr 1fr 2fr;
grid-template-rows: 50px 100px 60px;
padding: 20px;
background: pink;
}
上例中剩余空间大致按 1:1:2 分配,但内容最小尺寸、min-width、轨道最小值和最大值仍可能影响最终结果。

行或列的最小和最大尺寸
minmax(min, max) 可以定义网格轨道的最小值和最大值:
.grid-container {
display: grid;
grid-template-columns: 1fr 1fr 2fr;
grid-template-rows: minmax(100px, 1fr) minmax(50px, 1fr);
min-height: 300px;
padding: 20px;
background: pink;
}
minmax() 不会简单地把剩余空间按照某个固定公式平均分配。实际轨道尺寸还会受到内容、容器尺寸、对齐方式、间距和其他轨道的影响。
如果使用固定最大值:
grid-template-rows: minmax(100px, 200px) minmax(50px, 200px);
浏览器会按照 Grid Track Sizing Algorithm 计算轨道,不能直接套用“总高度减去最小值再除以 2”的推导。

重复行或列
repeat() 是一个函数,用于重复生成网格轨道:
.grid-container {
display: grid;
grid-template-columns: repeat(2, 100px);
grid-template-rows: repeat(3, 100px);
gap: 15px 50px;
padding: 20px;
background: pink;
}

还可以配合自动填充创建响应式网格:
.grid-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 1rem;
}
网格间距
现代写法使用 gap、row-gap 和 column-gap:
.grid-container {
display: grid;
grid-template-columns: repeat(2, 100px);
grid-template-rows: repeat(3, 100px);
column-gap: 50px;
row-gap: 15px;
background: pink;
}
grid-column-gap、grid-row-gap 和 grid-gap 是旧代码中常见的写法,现代项目推荐使用无布局模式专属前缀的 gap 系列属性。

通过网格线定位 Grid item
Grid item 可以使用网格线编号定位:
<div class="grid-container">
<div class="item item1">1</div>
<div class="item item2">2</div>
<div class="item item3">3</div>
<div class="item item4">4</div>
<div class="item item5">5</div>
<div class="item item6">6</div>
</div>
.grid-container {
display: grid;
grid-template-columns: repeat(2, 100px);
grid-template-rows: repeat(3, 100px);
column-gap: 50px;
row-gap: 15px;
padding: 20px;
background: pink;
}
.item {
border: 2px solid palegoldenrod;
color: white;
font-size: 20px;
text-align: center;
}
.item1 {
grid-row-start: 2;
grid-row-end: 3;
grid-column-start: 2;
grid-column-end: 3;
color: #000;
background: #fffa90;
}

grid-row 是 grid-row-start 和 grid-row-end 的简写,grid-column 是 grid-column-start 和 grid-column-end 的简写:
.item {
grid-row: 2 / 3;
grid-column: 3 / 4;
}
只提供一个值时,第二个值通常为 auto,不是同时设置起点和终点:
.item {
grid-row: 2; /* row-start: 2,row-end: auto */
grid-column: 3 / 4;
}
grid-area 的四个值依次是:
grid-area: row-start / column-start / row-end / column-end;
例如:
.item {
grid-area: 2 / 2 / 3 / 3;
}
合并网格行与网格列
可以使用起止网格线让一个项目占据多个轨道:
.item1 {
grid-column-start: 1;
grid-column-end: 3;
grid-row-start: 2;
grid-row-end: 4;
}

也可以使用 span 表示跨越的轨道数量:
.item1 {
grid-row: 2 / span 3;
grid-column: span 2;
}
grid-column: span 2 表示从自动确定的列起点开始跨越两列,不代表固定的列编号。
自定义网格线名称
Grid 线可以在轨道定义中设置名称,名称必须使用方括号:
.grid-container {
display: grid;
grid-template-columns:
[col1-start] 100px
[col1-end col2-start] 100px
[col2-end] 100px;
grid-template-rows:
[row1-start] 100px
[row1-end row2-start] 100px
[row2-end] 100px;
gap: 5px;
}
.a {
grid-column: col1-start / col2-end;
grid-row: row1-start;
}

线名称可以提高复杂布局的可读性,但名称要保持稳定,避免与项目中的业务命名混淆。
通过网格区域命名和定位项目
什么是网格区域?
网格区域(Grid Area)是由四条网格线围成的逻辑空间,可以包含一个或多个网格单元格。它可以通过网格线编号定义,也可以使用 grid-template-areas 命名。
使用 grid-template-areas
. 表示空的网格单元格,同一个区域名称可以跨越多个单元格,但必须组成矩形,不能组成 L 形或其他不规则形状:
<div class="layout">
<header class="header">header</header>
<main class="content">content</main>
<aside class="sidebar">sidebar</aside>
<footer class="footer">footer</footer>
</div>
.layout {
display: grid;
grid-template-columns: 200px repeat(4, 1fr);
grid-template-rows: 50px 150px 50px;
grid-template-areas:
'header header header header header'
'sidebar content content content content'
'footer footer footer footer footer';
gap: 5px;
text-align: center;
background: pink;
}
.header {
grid-area: header;
background: white;
}
.content {
grid-area: content;
background: #fffa90;
}
.sidebar {
grid-area: sidebar;
background: #5bc0de;
}
.footer {
grid-area: footer;
background: #ffff00;
}
原文的区域模板每行有 5 个单元格,却只定义了 3 列,已修正为 5 列,使区域模板和轨道数量一致。

同一个 Grid 行中的项目默认会拉伸到该行的交叉轴尺寸,因此内容较少的 sidebar 可能与 content 保持同一行高。但这不是因为两个元素内容高度自动相等,而是因为 Grid 项目的默认对齐方式通常为 stretch。设置 align-items: start 后,项目就不会自动拉伸。
常用的 CSS 布局:水平和垂直居中
居中方式不需要死记很多种写法,关键是判断:
- 子元素是固定尺寸还是内容尺寸;
- 需要水平居中、垂直居中还是同时居中;
- 是否允许使用 Flexbox 或 Grid;
- 是否需要兼容非常旧的浏览器;
- 元素是块级盒、行内盒还是行内块盒。
现代项目中,优先推荐 Flexbox 或 Grid。
固定尺寸元素的绝对定位居中
.container {
position: relative;
width: 300px;
height: 200px;
background: pink;
}
.inner {
position: absolute;
top: 50%;
left: 50%;
width: 100px;
height: 50px;
background: white;
text-align: center;
transform: translate(-50%, -50%);
}
这种方法适合元素尺寸固定或允许覆盖其他内容的场景。transform 的现代浏览器支持已经很好,不需要把 transformX、transformY 当作 CSS 属性;正确写法是 transform: translate(...)。
固定宽度元素的水平居中
普通流中的块级元素可以使用:
.inner {
width: 100px;
margin-inline: auto;
}
margin: 0 auto 只在适合的块级布局条件下实现水平居中,不能用于所有定位、浮动或行内元素。
宽度未知的行内级元素
父元素的 text-align: center 可以使行内级或行内块子元素水平居中:
.container {
text-align: center;
}
.inner {
display: inline-block;
}
它只影响行内级内容,不会自动使普通块级子元素居中。
多个子元素水平居中
.container {
display: flex;
justify-content: center;
gap: 10px;
}

使用 inline-block 也可以配合 text-align: center,但元素之间可能受源码空白影响;margin: 0 auto 对行内块元素通常不是主要的居中机制。
单行文本垂直居中
旧项目中可以让父容器的 line-height 等于高度,实现单行文字的近似垂直居中:
.container {
height: 200px;
line-height: 200px;
}
这种方法只适合单行文本。多行文本使用同样的 line-height 会导致行距过大,不应使用。
多行内容垂直居中:Flexbox
.container {
display: flex;
min-height: 400px;
align-items: center;
justify-content: center;
}

多行内容垂直居中:Table layout
为了兼容旧项目,可以使用表格布局:
.container {
display: table;
width: 200px;
height: 400px;
}
.inner {
display: table-cell;
vertical-align: middle;
}
vertical-align: middle 应该设置在 table-cell 上,而不是普通的 table 容器上。这个方案会改变布局模型,新项目通常优先使用 Flexbox。
原文还介绍了通过空的行内块元素占满高度的旧技巧。这种方法依赖行盒、基线和源码空白,代码复杂且容易受到内容宽度影响,不建议作为新项目的首选。
图片和文字垂直对齐
如果只是让同一行中的图片和文字相互对齐,可以使用 vertical-align: middle:
<div class="media">
<img src="example.jpg" alt="示例图片">
<span>图片旁边的文字</span>
</div>
.media {
font-size: 0;
}
.media img,
.media span {
display: inline-block;
vertical-align: middle;
font-size: 1rem;
}
vertical-align: middle 主要调整行内级元素相对于行盒的对齐,不等同于让整个组合在任意父容器中垂直居中。需要整体居中时,建议使用:
.media {
display: flex;
align-items: center;
gap: 0.5rem;
}

居中的原理总结
text-align: center主要作用于行内级内容和行内块子元素;vertical-align主要作用于行内级盒、行内块盒和表格单元格;line-height等于容器高度只适合单行文本;- 固定尺寸元素可以使用绝对定位和
transform; - 宽度未知的行内级元素可以使用
text-align; - 多行内容和未知尺寸内容优先使用 Flexbox 或 Grid;
position: relative的拼写要正确,绝对定位子元素需要相对定位包含块时才设置它;- 垂直居中不能只依赖一个通用属性,必须结合元素的布局上下文判断。
圣杯布局(Holy Grail Layout)
圣杯布局是经典的三栏布局:左右两栏固定宽度,中间栏自适应,并且中间内容通常在 DOM 中优先出现。

圣杯布局的历史实现依赖浮动、负外边距、父容器内边距和相对定位。现代项目通常使用 Grid 或 Flexbox,但了解它有助于维护旧代码和理解传统 CSS 布局。
圣杯布局的 HTML 结构
<div class="holy-grail">
<main class="middle">中间内容</main>
<aside class="left">left</aside>
<aside class="right">right</aside>
</div>
中间栏放在前面,是因为这种历史浮动方案依赖它先占据整行,再通过负外边距把左右栏移动到对应位置。这里的“先放置”是布局算法要求,不是为了让浏览器优先渲染。实际项目仍应优先考虑语义顺序和无障碍阅读顺序。
圣杯布局的传统实现
.holy-grail {
min-width: 600px;
padding: 0 200px;
box-sizing: border-box;
}
.holy-grail::after {
display: table;
clear: both;
content: '';
}
.middle,
.left,
.right {
float: left;
min-height: 200px;
box-sizing: border-box;
}
.middle {
width: 100%;
background: paleturquoise;
}
.left {
width: 200px;
margin-left: -100%;
background: palevioletred;
color: white;
}
.right {
width: 200px;
margin-left: -200px;
background: purple;
color: white;
}

这段代码需要通过父容器左右内边距给固定栏预留空间,并通过负外边距让左右栏移动到中间栏两侧。传统实现还常使用相对定位微调左右栏:
.left {
position: relative;
left: -200px;
}
.right {
position: relative;
right: -200px;
}


圣杯布局的主要问题是:当容器宽度小于左右栏总宽度时,中间栏可能过窄、内容重叠或出现横向滚动。因此需要设置合理的最小宽度,或者在小屏幕断点改为单列布局。

使用 Grid 实现圣杯布局
现代写法更简单:
.holy-grail {
display: grid;
grid-template-columns: 200px minmax(0, 1fr) 200px;
min-height: 200px;
}
.middle {
grid-column: 2;
background: paleturquoise;
}
.left {
grid-column: 1;
grid-row: 1;
background: palevioletred;
}
.right {
grid-column: 3;
grid-row: 1;
background: purple;
}
在窄屏幕上可以直接调整为:
@media (max-width: 640px) {
.holy-grail {
grid-template-columns: 1fr;
}
.left,
.middle,
.right {
grid-column: 1;
grid-row: auto;
}
}
双飞翼布局(Double-wing Layout)
双飞翼布局与圣杯布局都使用“中间栏在前、三栏浮动、左右栏使用负外边距”的思路。区别在于:
- 圣杯布局:通过父容器左右内边距,并使用左右栏的相对定位避免遮挡;
- 双飞翼布局:在中间浮动栏内部增加一个内容容器,通过内容容器的左右外边距为固定栏预留空间。
双飞翼布局结构
<div class="double-wing">
<div class="middle-container">
<div class="middle-content">中间内容</div>
</div>
<aside class="left">left</aside>
<aside class="right">right</aside>
</div>
.double-wing {
min-width: 600px;
}
.double-wing::after {
display: table;
clear: both;
content: '';
}
.middle-container,
.left,
.right {
float: left;
min-height: 200px;
box-sizing: border-box;
}
.middle-container {
width: 100%;
background: paleturquoise;
}
.middle-content {
margin: 0 200px;
}
.left {
width: 200px;
margin-left: -100%;
background: palevioletred;
color: white;
}
.right {
width: 200px;
margin-left: -200px;
background: purple;
color: white;
}

双飞翼布局也不是无限响应式的。当容器宽度小于左右栏总宽度时,仍然会出现溢出或重叠,因此需要设置最小宽度或在小屏幕下切换布局。

使用 Flex 或 Grid 替代
三栏布局使用 Flexbox 可以写成:
.layout {
display: flex;
}
.layout__left,
.layout__right {
flex: 0 0 200px;
}
.layout__middle {
flex: 1 1 auto;
min-width: 0;
}
使用 Grid 可以写成:
.layout {
display: grid;
grid-template-columns: 200px minmax(0, 1fr) 200px;
}
这两种方案不需要负外边距、浮动和清除浮动,更容易适配响应式断点。
提问与思考
原文最后提出了一个问题:对于左侧头像和下方文字垂直居中的布局,应该使用什么方法?

现代项目可以直接使用 Flexbox:
.profile {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
如果头像和文字需要横向排列:
.profile {
display: flex;
align-items: center;
gap: 0.75rem;
}
如果必须兼容 IE8 这样的旧环境,Flexbox 和 Grid 都不可用,只能使用表格布局、行内块加 vertical-align、绝对定位或传统浮动技巧。但这些方案应当被视为历史兼容方案,而不是现代项目的首选。
总结
- 普通流、浮动和定位是传统布局能力,页面整体布局不应只依赖浮动和绝对定位;
- Flexbox 主要解决一维布局,Grid 主要解决二维布局;
flex-shrink的初始值是1,不要求flex-wrap: nowrap才生效;align-content主要影响多行 Flex line,单行时通常没有明显效果;subgrid不是display值,而是 Grid 轨道定义中的关键字;- Grid 的行、列、轨道、单元格和区域需要区分;
grid-template-rows与grid-template-columns不要混写;minmax()的实际结果不能简单按照剩余空间平均推导;grid-template-areas的每一行必须有相同数量的单元格,区域名称应组成矩形;text-align、vertical-align、line-height有各自适用的布局上下文,不是通用居中属性;- 圣杯布局和双飞翼布局适合学习传统浮动,但现代项目应优先使用 Flexbox 或 Grid;
- 视觉重排不能破坏 DOM 顺序、语义和无障碍体验。