freeCodeCamp CSS Flexbox 教程实战:用 flex-direction: column 让弹性子项垂直堆叠
本文基于 freeCodeCamp 开源课程库中 css-flexbox 块 的第 5 个挑战(源文件:587d78ac367417b2b2512af4.md),系统讲解如何在弹性容器(flex container)中使用 flex-direction: column 把子元素改为纵向堆叠,并顺带剖析 freeCodeCamp 挑战文件的组织方式与自动判题原理。读完你既能独立完成该关卡,也能理解 freeCodeCamp 用一条 CSS 规则配合最小化 DOM 进行教学的设计思路,并可直接把同一套纵向排列技巧应用到自己的布局中。
这节课在 freeCodeCamp 课程体系中的位置
自由代码营把每个知识点封装成一份带结构化 frontmatter 的 Markdown 挑战文件。本课对应的文件是 curriculum/challenges/english/blocks/css-flexbox/587d78ac367417b2b2512af4.md,文件名本身即挑战 ID 587d78ac367417b2b2512af4。
在课程结构描述文件 curriculum/structure/blocks/css-flexbox.json 中,css-flexbox 块共编排了 17 个循序渐进的小挑战,覆盖 display: flex、flex-direction、justify-content、align-items、flex-wrap、flex-shrink、flex-grow、flex-basis、flex 简写、order、align-self 等核心知识点;本课(标题为 “Use the flex-direction Property to Make a Column”)是其中的第 5 个条目,紧跟在“Make a Row”与 Twitter 卡片行布局两个练习之后,属于 Responsive Web Design 超级块(该块在 responsive-web-design*.json 系列结构文件中被引用)下 HTML/CSS 方向的学习路径,块布局类型为 legacy-challenge-list。
认识 flex-direction:弹性容器决定主轴方向
本课描述开头直接点明前两个挑战已经把 flex-direction 设置成了 row。在进一步动手前,先厘清这套 CSS 机制:
- 给父元素加上
display: flex,它就变成一个弹性容器,其所有直接子元素自动成为弹性项目(flex items)。 flex-direction作用在父容器上,用来决定弹性项目沿主轴(main axis)的排列方向,从而把子元素排成行或列。- 默认值是
row(子元素水平排成一行);设为column后主轴变为垂直方向,子元素由上到下纵向堆叠成列。
参考同块前一个挑战 587d78ab367417b2b2512af2.md 的说明,flex-direction 一共有四个取值,可归纳如下:
| 取值 | 主轴方向 | 子元素视觉效果 |
|---|---|---|
row |
水平(从左到右) | 排成一行(默认值) |
row-reverse |
水平(从右到左) | 排成一行,起点换到右侧 |
column |
垂直(从上到下) | 纵向堆叠成一列 |
column-reverse |
垂直(从下到上) | 纵向堆叠成一列,起点换到底部 |
需要特别留意:column 并不是“没有布局”,而是把布局方向从水平切换为垂直——主轴与侧轴(cross axis)互换,主轴上的尺寸控制、对齐属性(如后文课程会讲的 justify-content 控制主轴方向)随之作用于垂直方向。
挑战目标与初始代码
本课的指令非常聚焦:
给
#box-container元素添加 CSS 属性flex-direction,并把值设为column。
挑战的初始代码(seed)提供了一个最小的实验环境:
<style>
#box-container {
display: flex;
height: 500px;
}
#box-1 {
background-color: dodgerblue;
width: 50%;
height: 50%;
}
#box-2 {
background-color: orangered;
width: 50%;
height: 50%;
}
</style>
<div id="box-container">
<div id="box-1"></div>
<div id="box-2"></div>
</div>
先分析这份 seed 的意图:
#box-container已经通过display: flex成为弹性容器,固定height: 500px,但目前没有声明flex-direction,因此采用默认值row——此时#box-1(道奇蓝)与#box-2(橘红)会水平并排在同一行内,尽管每个盒子声明了width: 50%与height: 50%。- 两个子盒子的尺寸都被设为容器的一半,且两色对比明显,能让学习者一眼看出布局方向的差异:
row时两个色块左右并排;改为column后主轴变成垂直方向,两个色块改为上下排列。
编写答案:一行 CSS 把排列改为纵向
按照指令,只需在 #box-container 的规则内补上一行,使其成为纵向布局:
<style>
#box-container {
display: flex;
height: 500px;
flex-direction: column;
}
#box-1 {
background-color: dodgerblue;
width: 50%;
height: 50%;
}
#box-2 {
background-color: orangered;
width: 50%;
height: 50%;
}
</style>
要点与效果说明:
flex-direction: column声明在父容器上,子元素无需任何改动即会响应方向变化——这正是弹性布局“由容器统一指挥子项排列方向”的核心特征。- 方向改为纵向后,主轴变为垂直:
#box-1与#box-2各占容器一半高度(在 500px 容器下约为各 250px),由上而下依次堆叠,形成清晰的纵向两段式色块。 - 修改前后唯一的差异就是这一条声明,充分说明
display: flex只负责“开启弹性上下文”,而真正的行/列排布由flex-direction决定。
顺带一提,若把值换成 column-reverse,两个盒子会交换上下顺序(橘红色块在上、蓝色块在下),这属于后续可自行验证的延伸实验。
自动判题如何验证你的答案
freeCodeCamp 对这类 HTML/CSS 练习采用浏览器内运行时测试。本课的 # --hints-- 区块给出了唯一一条判定规则:
const boxContainer = document.querySelector('#box-container');
const flexDirection = window.getComputedStyle(boxContainer)['flex-direction'];
assert.strictEqual(flexDirection, 'column');
这段测试代码的验证链路值得拆解:
document.querySelector('#box-container')定位被测元素;window.getComputedStyle(boxContainer)['flex-direction']读取该元素最终计算样式(computed style),即使flex-direction是通过样式表而非内联样式写入的也能正确读到;assert.strictEqual(flexDirection, 'column')做严格相等断言——若你没有添加该声明,计算值会是默认的row,测试即失败;只有显式设为column才通过。
从实现角度印证:本课 frontmatter 中的 challengeType: 0 对应共享配置 packages/shared/src/config/challenge-types.ts 中的 html 类型,而该类型在配置中映射为 classic 运行方式与 tests 判题模式,即这种“提交 HTML/CSS 后由浏览器执行断言”的交互练习。同时,课程仓库用 curriculum/schema/challenge-schema.js 对挑战文件的字段做 Joi 校验,其中 hints、seed、solutions 均为受约束的规范结构,保证每个挑战都能被统一的解析与执行管线处理。
参考实现
挑战文件末尾的 # --solutions-- 区块提供了官方参考答案,与本课的完整实现一致:
<style>
#box-container {
display: flex;
height: 500px;
flex-direction: column;
}
#box-1 {
background-color: dodgerblue;
width: 50%;
height: 50%;
}
#box-2 {
background-color: orangered;
width: 50%;
height: 50%;
}
</style>
<div id="box-container">
<div id="box-1"></div>
<div id="box-2"></div>
</div>
举一反三:在真实组件中把内容排成列
掌握了原理后,可以把同样的手法用于真实界面元素。紧邻本课的下一个挑战 587d78ac367417b2b2512af5.md 就要求在一个仿 Twitter 推文卡片中,对头部左侧的 .profile-name 元素(内含昵称 <h3> 与账号 <h4> 两行文字)设置 flex-direction: column,让“姓名在上、账号在下”自然纵向排列:
header .profile-name {
display: flex;
flex-direction: column;
margin-left: 10px;
}
该元素的判题同样通过 window.getComputedStyle(profileName)['flex-direction'] 断言其等于 column。注意其父级 header 依旧保持 flex-direction: row——这正是 flexbox 的常见用法:外层容器负责把头像、名称区、Follow 按钮排成一行,内层 .profile-name 再独立开辟自己的纵向上下文,嵌套容器各自声明方向互不干扰。
挑战文件格式解剖:作者视角的补充
对于想了解 freeCodeCamp 课程文件机制的读者,本课文件本身就是一份完整的最小样例,其 Markdown 结构如下:
- frontmatter:包含
id、title、challengeType、videoUrl、forumTopicId、dashedName等元数据;schema 校验规定dashedName必须匹配^[a-z0-9-]+$(见 curriculum/schema/challenge-schema.js 中的slugRE),forumTopicId为数字、videoUrl可为空字符串。 # --description--:用一两句话交代新知识点与前置知识的关系(本课指出“前两个挑战都用了row”)。# --instructions--:给出可执行的任务(添加并赋值flex-direction)。# --hints--:用真实浏览器断言定义“通过”标准,即本课前面展示的getComputedStyle检查。## --seed-contents--:学习者的起点代码,运行前由课程工具链解析、注入到可编辑页面。# --solutions--:官方参考实现,用于对照与后续的自动化测试。
因此,本课既是一道面向初学者的 5 分钟小练习,也是一份结构规范、可被工具链解析与自动判题的“可执行文档”。若想继续巩固该系列知识,可以直接沿 css-flexbox 块 的挑战顺序学习后续的 justify-content、align-items 与 flex-wrap 等课程,最终完整掌握 flexbox 的一维布局能力。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00