首页
/ freeCodeCamp 响应式网页设计认证项目实战:Build a Tribute Page(致敬页)完整通关指南

freeCodeCamp 响应式网页设计认证项目实战:Build a Tribute Page(致敬页)完整通关指南

2026-09-07 15:23:20作者:秋阔奎Evelyn

导读

本文围绕 freeCodeCamp 官方开源课程(本仓库)中响应式网页设计认证的第二个独立项目——**Build a Tribute Page(构建致敬页)**展开,逐条拆解挑战文档定义的 9 条 User Stories 与其配套的 11 组自动测试断言,并结合仓库内的结构配置与官方参考实现,给出可直接运行的 HTML/CSS 完整代码与语义化、响应式图片、无障碍等实践要点。学完本文,你将掌握:如何用正确的语义化标签 + 固定 id 结构通过全部自动校验、如何用 max-width/height: auto/display: block + 自动外边距实现不超原尺寸的居中响应式图片,以及新标签页外链的 target/rel 写法。

项目在课程体系中的定位

Tribute Page 项目是 freeCodeCamp 认证体系中最经典的 HTML/CSS 实战之一。在本仓库中,它的主体定义位于挑战源文件 bd7158d8c442eddfaeb5bd18.md,并以块(block)形式登记在课程结构元数据里:

  • 块结构配置:build-a-tribute-page-project.json 显示 helpCategoryHTML-CSSusesMultifileEditortruechallengeOrder 内唯一挑战 id 为 bd7158d8c442eddfaeb5bd18,即本挑战。
  • 超级块顺序:responsive-web-design-22.json 表明在 “Responsive Web Design” 超级块中,它位于五个独立认证项目中的第二位(前承 Survey Form,后接 Technical Documentation、Product Landing Page、Personal Portfolio)。

从挑战前置元数据看,其 challengeType: 14 属于共享包 challenge-types.ts 中定义的 multifileCertProject(多文件认证项目)类型。同一文件指出:

  • viewTypes[multifileCertProject] = 'classic':挑战以经典的多文件编辑器界面呈现,学习者需要在 htmlcss 两个文件面板中分别编写代码;
  • submitTypes[multifileCertProject] = 'tests':完成提交走“运行内置测试”流程,即下方的全部 # --hints-- 断言作为判定依据;
  • 该类型不在 hasNoSolution 排除清单中,因此官方在挑战文件中提供了 # --solutions-- 参考实现,可供学习与对照。

也就是说,这道题没有“标准唯一答案”,只要你的页面结构满足 User Stories、代码能通过全部测试,就算完成。

挑战目标与 9 条 User Stories 逐条解读

挑战文档给出的核心描述是:构建一个在功能上与官方演示项目类似的致敬页,但不得照抄演示代码。随后列出了驱动开发的 9 条验收标准,我们逐条翻译并给出对应的 HTML 语义建议:

  1. 页面必须有一个 id="main"<main> 元素,且所有其他内容元素都要放在它内部;
  2. 存在一个 id="title" 的元素,其中包含描述致敬对象的文本字符串(如 “Dr. Norman Borlaug”),且内容不能为空;
  3. 存在一个 id="img-div"<figure><div> 元素(图片容器);
  4. #img-div 内部必须有一个 id="image"<img> 元素;
  5. #img-div 内部还要有一个 id="img-caption" 的元素,存放对图片的文字说明——测试允许它是 <figcaption><div>
  6. 存在 id="tribute-info" 的元素,内放描述致敬对象的正文文字,且不能为空;
  7. 存在 id="tribute-link"<a> 外链,指向站外补充资料;必须target="_blank" 使其在新标签页打开;
  8. #image 必须用 max-widthheight 属性按父元素宽度进行响应式缩放,且不得超过图片原始尺寸;
  9. #image 必须在父元素内水平居中。

第 8、9 条是最容易踩坑的样式要求。文档原文在需求中已点明“responsive 于父元素宽度、不超过原始大小”,并用提示强调第 7 条 target="_blank" 的必要性。

测试断言即验收清单:代码会如何被检验

理解自动测试如何实现,能帮你一次性通过。挑战文件的 # --hints-- 区块实际就是检测脚本,全部运行在真实浏览器环境中(借助 document.getElementByIdwindow.getComputedStyle)。我们把断言归为三类并指出实现要点:

1. 元素存在性与嵌套关系(DOM 断言)

const el = document.getElementById('main');
assert.isNotNull(el);
assert.strictEqual(el.tagName, 'MAIN');

#img-div#image#img-caption#tribute-info#tribute-link 都必须是 #main 的后代,例如 document.querySelector('#main #image') 必须命中;#image 还必须是 #img-div 的后代,#img-caption 同理。结论:容器层级必须严格为 main > img-div > (image、img-caption),与 titletribute-infotribute-link 都平级放在 #main 下。

2. 文本非空断言

#title#img-caption#tribute-info 都会被检查 innerText/innerText.trim() 是否非空。因此不要使用空占位元素,也不要只塞图片不加说明文字。

3. 计算样式断言(CSS 效果的可验证标准)

这是本项目最独特的部分,测试并非“人眼比对”,而是读取浏览器最终计算样式:

const imgStyle = window.getComputedStyle(img);
assert.strictEqual(style, 'block');         // display 必须为 block
assert.strictEqual(style, '100%');          // max-width 必须为 100%
assert.strictEqual(heightValue, 'auto');    // height 必须为 auto

其中 height: auto 的检测还使用了“先把图片 display: none 再读取高度”的技巧(脚本注释注明取自 testable-projects 仓库),以排除布局干扰后验证真实计算值。居中的检测则比较 #image 与其父元素 getBoundingClientRect() 左右边距的差值小于 6px。

换言之,测试对写法提出了精确约束:居中不能靠 text-align,而要靠 margin 自动均分。官方参考 CSS 中正是这样组合:

#image {
  height: auto;
  display: block;
  margin: auto;      /* 让左右外边距自动均分实现居中 */
  max-width: 100%;
}

display: block 把图片变为块级盒,margin: auto 才能生效;max-width: 100% 保证在父容器小于原图宽度时收缩、原图宽度不足以填满时则保持原始尺寸;height: auto 保证缩放时按原图宽高比自适应,不发生形变。

推荐 HTML 骨架(语义化写法)

结合第 3、5 条中 “允许 figure/divfigcaption/div” 的弹性,推荐使用最语义化的 <figure> + <figcaption> 组合:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="styles.css" />
    <title>Dr. Norman Borlaug — Tribute Page</title>
  </head>
  <body>
    <main id="main">
      <h1 id="title">Dr. Norman Borlaug</h1>
      <figure id="img-div">
        <img
          id="image"
          src="https://upload.wikimedia.org/wikipedia/commons/..." 
          alt="Dr. Norman Borlaug standing in a wheat field"
        />
        <figcaption id="img-caption">
          Dr. Norman Borlaug, the father of the Green Revolution.
        </figcaption>
      </figure>
      <div id="tribute-info">
        <p>正文段落:介绍人物生平与贡献……</p>
        <p>
          了解更多:
          <a
            id="tribute-link"
            href="https://example.org/norman-borlaug"
            target="_blank"
            rel="noopener noreferrer"
            >维基百科相关词条</a
          >
        </p>
      </div>
    </main>
  </body>
</html>

要点对照:

  • 文档还特别提示:别忘了用 <link rel="stylesheet" href="styles.css"> 关联外部样式表;
  • 外链添加 target="_blank" 后,规范上应同时添加 rel="noopener noreferrer",避免新页面借助 window.opener 反向操作原页面(官方参考实现正是如此处理);
  • #tribute-link 不能是空链接,href 必须有值;#title 应描述致敬对象本身而非页面标题;
  • 注意 <h1 id="title"> 与正文标题的分工——很多初学者误把“Tribute Page”当 #title 文本,但需求示例要求的是人物名。

参考实现的 CSS 拆解

官方在挑战文件 # --solutions-- 区块给出一套以“卡片”为视觉形态的完整实现(HTML 部分内容较长,这里给出其可复现的核心结构)。CSS 关键样式如下:

main {
  background-color: #92869c;
  font-family: Lobster;
  max-width: 500px;
  margin: 20px auto;             /* 卡片居中 */
  color: black;
  border-radius: 50px;
  box-shadow: 10px 10px rgba(0, 0, 0, 0.5);
}
body {
  background-color: #3a3240;
  text-align: center;
  font-size: 12pt;
}
#image {
  height: auto;
  display: block;
  margin: auto;
  max-width: 100%;
  border-radius: 50%;
}
  • 页面背景与卡片背景构成深紫灰 + 灰紫的配色,卡片通过 border-radiusbox-shadow 制造圆角浮起效果;
  • 两套 Google Fonts(PacificoLobster)在 <head><link> 引入并分别赋给标题与正文,这是纯 CSS 课程阶段做字体排版的标准手法(示例中用 <link> 标签加载在线字体);
  • 边框统一用 .border 类(5px solid black)包裹图片,再叠加 border-radius: 50% 得到圆形头像;可注意到多个选择器重复定义了 body,这种写法不影响正确性,但你自己实现时合并会更整洁;
  • footer 居中、a:not(#tribute-link) 置白,目的是让导航链接与“主要外链”视觉区分。

测试如何被驱动:多文件认证项目的运行机制

前面提到本挑战属于 multifileCertProjectchallengeType: 14)。仓库在 build-a-tribute-page-project.json 中标注 usesMultifileEditor: true,意味着学习者在线上编辑器里拥有相互独立的 HTML 与 CSS 文件页签,二者实时组合渲染为页面预览;检测脚本随后在浏览器上下文内对渲染结果执行上文介绍的全部断言。也正因为是“tests 型提交”(见 challenge-types.tssubmitTypes 映射),通过/失败完全取决于这些可执行断言,而非人工审阅——这解释了为何文档要求“精确命中这些固定 id、固定计算样式”。

此外,块配置中的 blockLayout: "legacy-link" 表明该块在课程导航中按“项目型挑战”的旧式链接布局展示,即独立项目页面而非分步教程;同一 id 的挑战还可能以不同形态(如 Lab 变体)存在于其他超级块,但其作为“经典认证项目”的角色不变。

设计个性化致敬页的进阶建议

在满足测试约束的前提下,你可以自由发挥视觉:

  1. 无障碍:为 #image 添加有意义的 alt,并用 <figcaption> 承载图片说明;正文与背景对比度保持足够,标题层级从 h1 起连续使用;
  2. 移动优先布局:由于测试要求图片“不超过原始尺寸并随容器收缩”,你可以再为 #tribute-info 设置 max-widthline-height,配合 @media 断点调整字号,无需牺牲任何断言;
  3. 内容主题不限:致敬对象可以是历史人物、科学家、艺术家甚至虚构角色——官方参考实现就选用了一只宝可梦(Togepi)作为示例;
  4. 本地验证方式:将 HTML 与 CSS 存入同一目录,用浏览器打开 index.html 并保持 styles.css 同名相邻即可预览;若要核对测试,可对照上文三类断言逐一检查 DOM 与计算样式。

通关检查清单

完成实现后,请按以下清单自查(这也是对 # --hints-- 的浓缩):

  • [ ] 存在 <main id="main">,且 titleimg-divtribute-infotribute-link 都是其后代;
  • [ ] #img-divfigure/div,内含 #imageimg)与 #img-captionfigcaption/div);
  • [ ] #title#img-caption#tribute-info 均有非空文本;
  • [ ] #tribute-link 是带有效 href<a>,且 target="_blank",建议补 rel="noopener noreferrer"
  • [ ] CSS 中 #image { display: block; max-width: 100%; height: auto; margin: auto; } 四件套齐全;
  • [ ] 通过 <link rel="stylesheet" href="styles.css"> 关联样式表。

掌握本项目后,你会对“响应式图片四件套”、语义化结构与“测试即验收文档”的开发模式形成直观认知;后续的 Technical Documentation、Product Landing Page、Personal Portfolio 三个认证项目都沿用同一套“User Stories + 浏览器内断言”的交付方式,本项目的经验可以直接迁移。继续编写的每一步可随时对照本仓库中挑战源文件、块结构与共享类型配置进行求证。

<输出文章>

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391