- 示例工程
【免费下载链接】solved-by-flexbox
A showcase of problems once hard or impossible to solve with CSS alone, now made trivially easy with Flexbox.
本指南围绕开源项目 Solved by Flexbox(README.md)展开,它是一组以「曾经用纯 CSS 难以甚至无法解决、如今用 Flexbox 可轻松搞定」为主题的布局难题演示集合。文章将完整讲解项目的本地构建与运行方式,并逐一深入剖析网格系统、圣杯布局、媒体对象、输入附加组件、粘性页脚、垂直居中六个演示的痛点、HTML/CSS 实现与源码级细节,帮助你把这些方案直接复用到自己的项目中。
项目定位:把 Flexbox 的「逆袭」做成可运行演示
正如 README.md 第一句所定义的:"A showcase of problems once hard or impossible to solve with CSS alone, now made trivially easy with Flexbox."——这个仓库展示的是一批过去仅靠 CSS 很难甚至无法解决、如今用 Flexbox 却变得异常简单的布局问题。package.json 中的description字段也复述了同样的定位。
整个站点的核心内容位于 demos/ 目录,由六个带 front matter 元数据(template、title、excerpt)的 Markdown 文档构成,每个演示对应一套完整的「问题背景 + 演示 HTML + 核心 CSS + 跨浏览器兼容说明」:
| 演示文档 | 主题 | 对应组件样式 |
|---|---|---|
| demos/grids.md | 更简单、更好的网格系统 | assets/css/components/grid.css |
| demos/holy-grail.md | 圣杯布局 | assets/css/components/holy-grail.css |
| demos/media-object.md | 媒体对象 | assets/css/components/media.css |
| demos/input-add-ons.md | 输入框附加组件 | assets/css/components/input-add-on.css |
| demos/sticky-footer.md | 粘性页脚 | assets/css/components/site.css |
| demos/vertical-centering.md | 垂直居中 | assets/css/components/aligner.css |
在继续之前,先掌握如何把整个演示站点在本地跑起来——这是阅读下文所有示例、亲手验证每个 Flexbox 效果的前提。
本地构建与运行:十分钟起一个可热更新的演示站点
README.md 提供了完整的本地运行方式,前提是本机已安装 Node.js。克隆仓库(此处以当前镜像地址为例)后依次执行:
# 克隆仓库并进入目录 git clone https://gitcode.com/gh_mirrors/so/solved-by-flexbox.git cd solved-by-flexbox # 安装依赖 npm install # 构建并在 http://localhost:4000 提供服务 npm start服务启动后,浏览器访问http://localhost:4000即可查看站点。需要更换端口时,把端口号作为参数传给npm start:
npm start -- -p 8080README 特别强调:npm start除了构建并本地服务外,还会持续监听文件变化并自动重建——修改代码后刷新浏览器即可立即看到效果。这一点在构建脚本中得到了印证:查看 gulpfile.js 的serve任务(第 160-168 行),它会用gulp.watch分别监听assets/css/**/*.css、assets/images/*、assets/main.js、根目录*.html、demos/*与templates/*,任一变化都会触发对应子任务增量重建;默认端口逻辑为argv.port || argv.p || 4000。
如果你只关心「构建」而不启动服务,可以运行npm run build(对应package.json中的"build": "gulp")。整个构建流程由 gulpfile.js 的default任务并行执行四条子流水线(第 158 行):
css任务:以 assets/css/main.css 为入口,经postcss-import合并样式、postcss-preset-env(stage: 0)编译 CSS 变量与自定义媒体查询,浏览器目标为> 1%, last 2 versions, Safari > 5, ie > 9, Firefox ESR(gulpfile.js);images任务:复制assets/images/**/*到构建目录;javascript任务:执行rollup -c,由 rollup.config.js 以assets/main.js为入口打包为 ESM 模块;pages任务:用front-matter解析每个demos/*.md的元数据,markdown-it渲染正文(配highlight.js代码高亮),再交给nunjucks套用 templates/ 下的模板,最终输出为demos/<slug>/index.html;根目录*.html(如 index.html)同样参与渲染(gulpfile.js)。
所有产物统一输出到./solved-by-flexbox目录(gulpfile.js中的DEST常量),页面permalink规则为:index页落在baseUrl,其余演示页落在demos/<slug>/(gulpfile.js)。若需生产环境构建,可设置NODE_ENV=production——此时pages任务会叠加html-minifier压缩,css任务叠加cssnano压缩,rollup会启用terser压缩(gulpfile.js、rollup.config.js);npm run deploy脚本则要求NODE_ENV=production否则直接抛错(gulpfile.js)。
六大经典布局难题的 Flexbox 解法
下面六个小节是项目的技术主体。每个演示都遵循同一模式:先说明传统 CSS 方案的痛点,再给出 Flexbox 的极简实现,最后补充仓库源码中的完整细节。
1. 网格系统:告别 float 与 inline-block 的围剿
demos/grids.md 指出,绝大多数网格系统至今仍依赖float或inline-block两种布局手段,而它们本都不是为布局设计的,因而问题重重:
float需要清除浮动,清除行为会带来一系列布局问题,最典型的是清除元素有时会被强制推到页面某个无关区域的下方;此外清除浮动通常需要同时占用::before/::after两个伪元素,导致伪元素无法再用于其他用途;inline-block必须处理元素之间的空白字符,而现有的各种解决方案都相当 hacky。
Flexbox 不仅消除了这些问题,还打开了全新的可能性。作者在文档中给出了理想网格系统的六条标准,而这些特性 Flexbox 几乎白送:
- 默认情况下,每个单元格与同行其他单元格等宽等高(自动均分空间);
- 需要精细控制时,可给单个单元格添加尺寸类,未设尺寸的单元格照常均分剩余空间;
- 响应式网格可为单元格添加媒体查询专属类;
- 单个单元格可垂直对齐到顶部、底部或中间;
- 当整行网格的尺寸、媒体或对齐值一致时,只需给容器加一个类,避免重复;
- 网格可以任意深度嵌套。
基础网格的 HTML 极其简洁,.Grid容器包住若干.Grid-cell即可:
<div class="Grid"> <div class="Grid-cell">…</div> <div class="Grid-cell">…</div> <div class="Grid-cell">…</div> </div>对应的核心 CSS 只有两条规则:
.Grid { display: flex; } .Grid-cell { flex: 1; }flex: 1让所有单元格自动均分容器宽度,align-items的默认值stretch又让它们天然等高。仓库中的完整实现(assets/css/components/grid.css)在此基础上还包含flex-wrap: wrap,支持换行。
单个单元格定制尺寸:演示 HTML 中可直接给单元格叠加u-1of2、u-1of3、u-1of4等工具类(demos/grids.md 的 Individual Sizing 小节),未加尺寸类的「auto」单元格则自动吃掉剩余空间。若希望把尺寸规则统一挂在容器上,源码也提供了容器级修饰符:
.Grid--fit > .Grid-cell { flex: 1; } .Grid--full > .Grid-cell { flex: 0 0 100%; } .Grid--1of2 > .Grid-cell { flex: 0 0 50%; } .Grid--1of3 > .Grid-cell { flex: 0 0 33.3333%; } .Grid--1of4 > .Grid-cell { flex: 0 0 25%; }响应式网格(移动优先):文档给出的示例是「默认全宽、48em以上自适应」。原理是先用Grid--full(flex: 0 0 100%)做基础全宽,再用large-Grid--fit(flex: 1)在@media (min-width: 48em)下让单元格按需分栏。源码中的断点并非硬编码,而是使用@media (--break-sm)、@media (--break-md)、@media (--break-lg)等自定义媒体查询变量(定义在 assets/css/base/custom-media.css),由postcss-preset-env在构建期编译成真实断点。
嵌套(Grid-ception):.Grid组件可以在任意层级内无限嵌套,演示中甚至出现了三层嵌套组合。
对齐能力:文档与源码共同展示了两个层级的对齐控制——
/* 按行对齐 */ .Grid--top { align-items: flex-start; } .Grid--bottom { align-items: flex-end; } .Grid--center { align-items: center; } /* 按单元格对齐 */ .Grid-cell--top { align-self: flex-start; } .Grid-cell--bottom { align-self: flex-end; } .Grid-cell--center { align-self: center; }容器级类通过align-items统一整行对齐方式,单元格级类通过align-self覆盖单个单元格。源码还额外提供Grid--justifyCenter(justify-content: center)与Grid-cell--autoSize(flex: none)两个实用修饰符,以及Grid--gutters(margin: -1em 0 1em -1em+ 子元素padding: 1em 0 0 1em)、Grid--guttersLg(1.5em)、Grid--guttersXl(2em)三档间距方案,并各自带有small-/med-/large-前缀的响应式版本。
2. 圣杯布局:三栏等高的终极方案
圣杯布局(Holy Grail Layout)是 Web 设计史上的经典难题,demos/holy-grail.md 将其核心目标概括为五条:
- 中间栏流体自适应,左右侧栏固定宽度;
- 中间主内容列在 HTML 源码中必须排在第一位;
- 无论哪一列最高,三列高度始终一致;
- 标记尽量精简;
- 内容稀少时页脚「贴」在页面底部。
由于这些目标彼此牵制,加上旧 CSS 的能力边界,历史上没有任何经典方案能同时满足全部五条。Flexbox 让完整解法首次成为可能。
HTML 结构(注意main位于nav、aside之前):
<body class="HolyGrail"> <header>…</header> <div class="HolyGrail-body"> <main class="HolyGrail-content">…</main> <nav class="HolyGrail-nav">…</nav> <aside class="HolyGrail-ads">…</aside> </div> <footer>…</footer> </body>CSS:让中间内容行拉伸、让页脚贴底,用的正是与 demos/sticky-footer.md(粘性页脚演示)相同的手法,唯一区别是圣杯布局的中间行.HolyGrail-body需要display: flex来排列自己的三个子元素:
.HolyGrail { display: flex; min-height: 100vh; flex-direction: column; } .HolyGrail-body { display: flex; flex: 1; }三栏等高、中间流体、两侧固定,同样只需三组规则:
.HolyGrail-content { flex: 1; } .HolyGrail-nav, .HolyGrail-ads { /* 12em 是侧栏的宽度 */ flex: 0 0 12em; } .HolyGrail-nav { /* 把导航放到左侧 */ order: -1; }order: -1让 HTML 中排在第二位的导航在视觉上移到最左,从而在不改源顺序的前提下满足「主内容优先」的目标;flex: 0 0 12em用flex-basis固定侧栏宽度,flex-shrink: 0保证它们不会被压缩。
响应式改造:圣杯布局诞生于桌面浏览器时代,如今更需要移动优先版本。文档给出的思路是:默认把中间区设为flex-direction: column纵向堆叠,较大屏幕再切回row:
.HolyGrail, .HolyGrail-body { display: flex; flex-direction: column; } .HolyGrail-nav { order: -1; } @media (min-width: 768px) { .HolyGrail-body { flex-direction: row; flex: 1; } .HolyGrail-content { flex: 1; } .HolyGrail-nav, .HolyGrail-ads { /* 12em 是侧栏的宽度 */ flex: 0 0 12em; } }仓库中的完整实现 assets/css/components/holy-grail.css 在示例基础上补充了跨浏览器细节(见文件头部注释):用height: 100%规避 IE 10-11 的min-heightbug,并为头部/页脚设置flex: none防止被浏览器压缩到小于内容默认最小尺寸,同时把vh换成%以兼容旧版移动 Safari。
3. 媒体对象:去掉 BFC 与 clearfix 的拐杖
媒体对象(Media Object)是面向对象 CSS(OOCSS)的招牌模式,但demos/media-object.md 指出,传统实现必须借助技巧:正文要么创建块格式化上下文(BFC),要么用与图片同宽的外边距/内边距来阻止文字绕排;同时还要对正文做 clearfix,只能用overflow: hidden或占用伪元素。Flexbox 一举解决全部问题,还顺带解锁了两个新能力:图片(figure)可任意垂直对齐;不修改源顺序即可把图片放到右侧。
基础结构:
<div class="Media"> <img class="Media-figure" src="" alt=""> <p class="Media-body">…</p> </div>核心 CSS:
.Media { display: flex; align-items: flex-start; } .Media-figure { margin-right: 1em; } .Media-body { flex: 1; }flex: 1让正文占据剩余宽度,文字永远不会绕排到图片下方;align-items: flex-start让图片默认顶对齐。完整源码 assets/css/components/media.css 还提供了两个增强修饰符:
.Media--center { align-items: center; /* 图片垂直居中 */ } .Media--reverse > .Media-figure { order: 1; /* 图片移到右侧 */ margin: 0 0 0 1em; }Media--reverse仅靠order: 1就完成了图文左右互换,完全不用调整 DOM 顺序。文档中的演示还覆盖了「非图片 figure」(如图标)、以及多层嵌套媒体对象(评论回复等典型场景),这些都直接复用同一套.Media结构。
4. 输入附加组件:流体 input 与按钮/标签的无缝拼接
demos/input-add-ons.md 描述了一个长期无解的痛点:受 CSS 输入框尺寸行为限制,几乎不可能在 input 前后追加元素并让输入框流体占满剩余宽度。旧方案要么必须预先知道 input 的精确宽度,要么使用display: table-cell——后者在部分浏览器中很难对附加元素内部做绝对定位。Flexbox 让这些问题全部消失,代码极其简单,并且免费获得「输入框与附加元素等高」的效果。
支持前附加、后附加、两侧同时附加三种形态:
<!-- 后附加(appending) --> <div class="InputAddOn"> <input class="InputAddOn-field"> <button class="InputAddOn-item">…</button> </div> <!-- 前附加(prepending) --> <div class="InputAddOn"> <span class="InputAddOn-item">…</span> <input class="InputAddOn-field"> </div> <!-- 两侧都附加 --> <div class="InputAddOn"> <span class="InputAddOn-item">…</span> <input class="InputAddOn-field"> <button class="InputAddOn-item">…</button> </div>核心 CSS:
.InputAddOn { display: flex; } .InputAddOn-field { flex: 1; /* 输入框样式 */ } .InputAddOn-item { /* 附加元素样式 */ }完整的 assets/css/components/input-add-on.css 还处理了视觉衔接细节:利用:not(:first-child)/:not(:last-child)合并相邻元素的边框(border-left: 0/border-right: 0),并为首尾元素分别设置border-radius: 2px 0 0 2px与0 2px 2px 0,附加元素使用半透明背景rgba(147, 128, 108, 0.1)与主输入框区分。
5. 粘性页脚:页脚高度未知也不怕
让页脚在内容稀少的页面上「贴」在窗口底部,几乎是每个 Web 开发者都尝试过的问题,且已有不少成熟方案。但demos/sticky-footer.md 点出了它们共同的致命短板:页脚高度未知时全部失效。Flexbox 恰好是这类问题的完美匹配——它不只为水平方向设计,垂直布局同样擅长:只要把纵向区块包进一个 flex 容器,再指定哪个区块需要扩展,它就会自动占据容器内的全部剩余空间。
一个关键差异是:垂直方向需要为容器显式指定高度(水平方向会自动扩展撑满)。示例结构:
<body class="Site"> <header>…</header> <main class="Site-content">…</main> <footer>…</footer> </body>CSS:
.Site { display: flex; min-height: 100vh; flex-direction: column; } .Site-content { flex: 1; }min-height: 100vh保证页面至少与视口等高,flex: 1让主内容区吃掉所有剩余高度,页脚自然被推到最底部。仓库源码 assets/css/components/site.css 中的跨浏览器版本做了三处加固:用height: 100%替代min-height: 100vh(规避 IE 10-11 的 min-height bug 与旧版移动 Safari 的 vh 问题);Site-header/Site-footer设flex: none防止被压缩;Site-content使用flex: 1 0 auto(flex-shrink: 0),并用::after插入一个 空字符占位块来保证内容区底部留白。文档中的演示还附带了一个「Toggle Contents」按钮的交互脚本(demos/sticky-footer.md),用来直观验证内容隐藏后页脚依然贴底。
6. 垂直居中:内容尺寸未知、有兄弟元素也能居中
demos/vertical-centering.md 开篇就指出:CSS 缺乏优雅的垂直居中手段,是其历史上的一大污点。更糟的是,那些真正能用的技术(负外边距、display: table-cell、全高伪元素 hack)既晦涩又不直观,而最直观的vertical-align: middle在需要时几乎从不生效;并且这些老办法都有限制——目标元素尺寸未知怎么办?它不是父元素唯一子元素怎么办?伪元素被占用怎么办?
Flexbox 用align-items、align-self、justify-content三个属性让这一切变得毫无压力——任意元素、任意尺寸、任意兄弟关系,都能轻松实现垂直和/或水平居中。
HTML:
<div class="Aligner"> <div class="Aligner-item Aligner-item--top">…</div> <div class="Aligner-item">…</div> <div class="Aligner-item Aligner-item--bottom">…</div> </div>CSS:
.Aligner { display: flex; align-items: center; justify-content: center; } .Aligner-item { max-width: 50%; } .Aligner-item--top { align-self: flex-start; } .Aligner-item--bottom { align-self: flex-end; }容器用align-items: center+justify-content: center实现双轴居中;单个元素可用align-self覆盖为自己的对齐方式,从而在同一个容器内混排「顶对齐 / 居中 / 底对齐」。这与传统方案形成鲜明对比:兄弟元素的存在完全不影响彼此的垂直对齐能力。演示中还提供了可contenteditable的示例框,编辑文字使其变宽变高,盒子依然保持居中。完整实现见 assets/css/components/aligner.css,其中.Aligner-item为flex: 1,另有.Aligner-item--fixed(flex: none; max-width: 50%)用于固定尺寸项;容器设置了min-height: 24em以确保演示区域有足够的对齐空间。
跨浏览器兼容与工具链:demo 之外的真实代码
需要特别留意的是,文档中的示例代码假设浏览器完全符合规范,而仓库源码为了兼容真实浏览器做了额外处理——每个演示文档都在显眼位置以 Note 形式提示了这一点,并指向源码注释:
- 圣杯布局与粘性页脚的兼容细节见 assets/css/components/holy-grail.css 与 assets/css/components/site.css 文件头部的注释(IE 10-11
min-heightbug、flex-shrink置 0、vh→%); - 垂直居中的兼容标记与处理见 assets/css/utils/compat.css。
构建工具链为这套「规范示例 + 兼容源码」的双轨策略提供了支撑:样式统一以 assets/css/main.css 为入口,由 gulpfile.js 的 css 任务交给postcss-preset-env(stage: 0)处理 CSS 变量与自定义媒体查询,生产环境再用cssnano压缩;依赖清单中的normalize.css提供基础样式重置,highlight.js负责代码块高亮,markdown-it渲染演示正文,nunjucks套用 templates/ 下的页面模板(如 templates/default.html、templates/holy-grail.html),站点级数据则来自 config.json。
社区翻译与继续深入
README 还记录了一个社区贡献事实:该项目已有中文、日文、韩文等社区翻译版本,同时明确提示这些非官方翻译可能不够准确或已过时;若想提交自己的翻译,可向项目提交 pull request 或打开 issue 并附上译文链接。
进一步学习时,建议按「文档 → 源码 → 构建配置」三层递进阅读:
- 逐个精读 demos/ 下的六个 Markdown 文档,先理解每个问题的历史痛点;
- 对照 assets/css/components/ 下的组件样式,观察示例与生产级代码的差异(尤其是兼容性注释);
- 通读 gulpfile.js、package.json、rollup.config.js,理解文档是如何被渲染、压缩并发布成静态站点的。
把六个演示中的.Grid、.HolyGrail、.Media、.InputAddOn、.Site、.Aligner组件抽出来,稍作变量化调整即可直接嵌入你自己的项目——这正是本项目「solved by flexbox」最大的实用价值所在。
- 示例工程
【免费下载链接】solved-by-flexbox
A showcase of problems once hard or impossible to solve with CSS alone, now made trivially easy with Flexbox.
相关推荐
Flexbox实战指南:solved-by-flexbox项目教你攻克10大CSS布局难题
Flexbox实战指南:solved by flexbox项目教你攻克10大CSS布局难题 在CSS布局领域,Flexbox(弹性盒模型)的出现彻底改变了前端开
示例工程如何配置Lucky反向代理:一个监听端口带起多台服务
如何配置Lucky反向代理:一个监听端口带起多台服务 家里几台机器各跑着一个 Web 服务,要记的却是一串 192.168.31.80:28888 这样的地址加
后端网络通信Flexbox布局与CSS变量结合:solved-by-flexbox的主题切换实现
Flexbox布局与CSS变量结合:solved by flexbox的主题切换实现 在前端开发中,布局与样式的动态调整一直是提升用户体验的关键。solved
示例工程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考