freeCodeCamp CSS Grid 实战:用媒体查询打造响应式 Grid 布局
【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp
本文基于 freeCodeCamp 开源课程中 Responsive Web Design 课程的 CSS Grid 练习「Use Media Queries to Create Responsive Layouts」(课程文件 curriculum/challenges/english/blocks/css-grid/5a94fe7769fb03452672e463.md),完整拆解如何用grid-template-areas配合媒体查询,在同一个 Grid 容器上实现三档断点下的布局重排。读完后你能掌握「区域模板 + 断点覆盖」这套响应式 Grid 的核心写法,并理解课程平台如何用正则断言自动校验你的答案。
这节课在课程中的位置
freeCodeCamp 的课程内容以 Markdown 文件组织,CSS Grid 是一个独立的 block(模块),其结构定义在 curriculum/structure/blocks/css-grid.json 中,共包含 21 个按challengeOrder排列的练习,从「Create Your First CSS Grid」一路讲到「Create Grids within Grids」。本练习排在倒数第二位,前置知识(grid-template-areas、grid-area、minmax、auto-fill/auto-fit)均已在前面几个练习中完成铺垫。整个 css-grid block 隶属于 Responsive Web Design 超级模块,其引用可见于 curriculum/structure/superblocks/responsive-web-design.json;仓库中另有一份新版学习流 curriculum/structure/superblocks/responsive-web-design-22.json,采用「边做项目边学」的编排(如 learn-css-grid-by-building-a-magazine),从源码结构看,两种编排并行存在于当前仓库中。
每个练习文件的头部是 YAML frontmatter,本练习的元信息为:
id: 5a94fe7769fb03452672e463 title: Use Media Queries to Create Responsive Layouts challengeType: 0 videoUrl: 'https://scrimba.com/p/pByETK/cMbqeHk' forumTopicId: 301138 dashedName: use-media-queries-to-create-responsive-layouts其中dashedName用于生成学习页面的 URL 段,forumTopicId关联课程官方论坛讨论串,videoUrl挂载配套讲解视频(外部资源,此处仅说明字段用途)。frontmatter 的合法性由 curriculum/schema/challenge-schema.js 中的 Joi schema 统一校验,保证课程文件在构建时字段齐全、格式规范。
前置回顾:区域模板与 grid-area
本练习的起点是一个已经建好的 Grid 布局。回顾一下其核心机制(完整讲解放在 5a94fe0569fb03452672e45c.md 与 5a94fe1369fb03452672e45d.md 两个练习中):
- 容器侧用
grid-template-areas声明一个「文字画板」:每一对引号代表一行,每个单词代表一个单元格,相同名称的相邻单元格组成一个命名区域。 - 项目侧用
grid-area: 区域名声明自己放进哪个区域。
本练习的四个项目(header / advert / content / footer)各自只声明了背景色和grid-area,从不断点变化——所有响应式行为都集中在.container上,这是「区域模板 + 断点覆盖」写法的精髓:改布局时只动容器,不动项目。
核心:三种断点下的布局演进
练习的 description 指出:CSS Grid 是响应式布局的便捷手段,可以通过媒体查询来「重排网格区域、改变网格尺寸、重新安排项目位置」。整个练习的 HTML 结构自始至终是:
<div class="container"> <div class="item1">header</div> <div class="item2">advert</div> <div class="item3">content</div> <div class="item4">footer</div> </div>布局变化全部由三段 CSS 驱动,理解这三段是理解本节的关键。
断点 0:默认单列布局
.container { font-size: 1.5em; min-height: 300px; width: 100%; background: LightGray; display: grid; grid-template-columns: 1fr; grid-template-rows: 50px auto 1fr auto; grid-gap: 10px; grid-template-areas: 'header' 'advert' 'content' 'footer'; }视口小于 300px 时生效:单列(1fr),四行从上到下依次是 header、advert、content、footer,其中 header 固定 50px、content 用1fr吃掉剩余空间。注意每一行的区域字符串都只有 1 个名称,与grid-template-columns: 1fr的 1 条列轨一一对应。
断点 1:≥ 300px,广告区占满左列
@media (min-width: 300px) { .container { grid-template-columns: auto 1fr; grid-template-rows: auto 1fr auto; grid-template-areas: 'advert header' 'advert content' 'advert footer'; } }视口宽度达到 300px 后:列轨变为「auto + 1fr」两列,行轨变为 3 行。区域模板里advert连续出现在每一行的第一个位置,因此广告区纵向贯通、占满整个左列;header/content/footer 依次落在右列的三行。这正是文档 description 中「the advertisement area then occupies the left column completely」所指的效果。
断点 2:≥ 400px,练习目标——header 通栏在上、footer 通栏在下
这是练习的 instructions 原文要求:
When the viewport width is
400pxor more, make the header area occupy the top row completely and the footer area occupy the bottom row completely.
seed 代码中已给出媒体查询的「壳」,要求只在注释标记的区域内修改grid-template-areas:
@media (min-width: 400px) { .container { grid-template-areas: /* Only change code below this line */ 'advert header' 'advert content' 'advert footer'; /* Only change code above this line */ } }正确答案是(即练习的 --solutions-- 区块):
@media (min-width: 400px) { .container { grid-template-areas: 'header header' 'advert content' 'footer footer'; } }此时布局变为「经典门户页」形态:header 横跨两列占满顶行,footer 横跨两列占满底行,中间行左侧 advert、右侧 content。
这里有一个容易被忽略的机制细节:400px 断点只重写了grid-template-areas,并没有重复声明grid-template-columns和grid-template-rows。这是正确的——媒体查询不是相互独立的「页面」,而是按声明顺序叠加的普通规则块。达到 400px 时,300px 块里的两条声明(grid-template-columns: auto 1fr;、grid-template-rows: auto 1fr auto;)依然生效,400px 块只需覆盖真正发生变化的区域模板即可。若把grid-template-columns: 1fr(单列)也带进 400px 块,每行 2 个名称的区域模板就会与 1 条列轨失配,布局反而崩溃。
对应地,区域模板的书写约束是:每行的名称数量必须与列轨数量一致——单列布局每行 1 个名称,两列布局每行 2 个名称,想让某个区域横跨多列就重复写该名称('header header'表示 header 区域横跨两列)。
完整练习代码:从 seed 到 solution
以下是练习的完整 seed(初始状态,含待修改区域):
<style> .item1 { background: LightSkyBlue; grid-area: header; } .item2 { background: LightSalmon; grid-area: advert; } .item3 { background: PaleTurquoise; grid-area: content; } .item4 { background: lightpink; grid-area: footer; } .container { font-size: 1.5em; min-height: 300px; width: 100%; background: LightGray; display: grid; grid-template-columns: 1fr; grid-template-rows: 50px auto 1fr auto; grid-gap: 10px; grid-template-areas: 'header' 'advert' 'content' 'footer'; } @media (min-width: 300px) { .container { grid-template-columns: auto 1fr; grid-template-rows: auto 1fr auto; grid-template-areas: 'advert header' 'advert content' 'advert footer'; } } @media (min-width: 400px) { .container { grid-template-areas: /* Only change code below this line */ 'advert header' 'advert content' 'advert footer'; /* Only change code above this line */ } } </style> <div class="container"> <div class="item1">header</div> <div class="item2">advert</div> <div class="item3">content</div> <div class="item4">footer</div> </div>完整 solution 与 seed 的唯一差异在 400px 块的区域模板:
@media (min-width: 400px) { .container { grid-template-areas: 'header header' 'advert content' 'footer footer'; } }平台的自动校验:一条正则断言
freeCodeCamp 的练习在提交后由代码级测试判定。本练习的 hints 区块给出了判定逻辑——它不是执行 CSS,而是用正则匹配你提交的代码文本(先经__helpers.removeCssComments去掉 CSS 注释,避免注释干扰匹配):
assert.match( __helpers.removeCssComments(code), /@media\s*?\(\s*?min-width\s*?:\s*?400px\s*?\)[\s\S]*.container\s*?{[\s\S]*grid-template-areas\s*?:\s*?["|']\s*?header\s*?header\s*?["|']\s*?["|']\s*?advert\s*?content\s*?["|']\s*?["|']\s*?footer\s*?footer\s*?["|']\s*?;[\s\S]*}/gi );拆解这条正则的匹配意图:
@media\s*?\(\s*?min-width\s*?:\s*?400px\s*?\)——定位到@media (min-width: 400px)块(\s*?容忍任意空白,[:\s]?兼容冒号前后空格);- 其后必须在
.container选择器内找到grid-template-areas; - 区域模板必须按行依次是
'header header'、'advert content'、'footer footer',引号允许单引号或双引号(["|'])。
这条断言也间接确认了答案形态:header 必须「两名横排」而非其他方式通栏。hints 的文字提示与之一致:达到 400px 时 header 与 footer 分别占据顶/底行,advert 与 content 占据中间行的左/右列。
小结与延伸阅读
- 响应式 Grid 的最小改动面:保持项目(item)的
grid-area声明不变,只在容器的媒体查询块中重写grid-template-areas,即可实现断点间的整体重排; - 媒体查询是叠加而非替换:较宽断点只需声明发生变化的属性,未声明的属性沿用先前规则块的值;
- 区域模板与列轨必须对齐:每行的区域名数量等于列轨数量,重复名称即跨列。
围绕本练习,仓库中可直接继续阅读的相关材料:
- 前置练习:划分区域模板、用 grid-area 放置项目、minmax 限制项目尺寸、auto-fill 弹性布局、auto-fit 弹性布局;
- 后续练习(本节的下一课):Create Grids within Grids,它直接复用了本练习 300px 断点的布局作为起点,把 content 区域本身再嵌套成一个 Grid;
- 课程结构:css-grid block 定义、Responsive Web Design 模块;
- 内容规范:challenge-schema.js 定义了练习文件 frontmatter 的校验规则。
需要说明的是:以上断点阈值(300px / 400px)是该教学示例为了让布局变化在小尺寸预览面板中肉眼可辨而选取的值,实际生产项目中应基于真实内容排版与设备分布选取断点,而非照搬数值。
【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考