freeCodeCamp 响应式网页设计认证挑战全解:Build a Product Landing Page 产品落地页项目

发布时间:2026/9/8 22:05:38
freeCodeCamp 响应式网页设计认证挑战全解:Build a Product Landing Page 产品落地页项目 freeCodeCamp 响应式网页设计认证挑战全解Build a Product Landing Page 产品落地页项目【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp导读本指南以 freeCodeCamp 开源课程中响应式网页设计Responsive Web Design认证的核心结业项目Build a Product Landing Page产品落地页为题逐条拆解其 挑战源文档 中的 15 条用户故事、20 组自动化验收断言与官方参考实现并结合仓库内课程结构配置与测试基础设施说明每条需求背后的 DOM/CSS 判定原理。读完本文你将能独立完成一个同时满足功能、可访问性与响应式布局要求的单页产品营销网站并彻底理解它的打分机制。一、挑战定位这份挑战文件在课程体系中的位置先看该挑战的 YAML Front Matter 头信息id: 587d78af367417b2b2512b04 title: Build a Product Landing Page challengeType: 14 saveSubmissionToDB: true forumTopicId: 301144 dashedName: build-a-product-landing-pagechallengeType: 14对照仓库 packages/shared/src/config/challenge-types.ts 中的枚举14对应multifileCertProject多文件结业项目即它属于需同时提交 HTML 与 CSS 两个文件、由自动化测试验收的认证型项目saveSubmissionToDB: true提交结果会持久化入库服务端通过 api/src/utils/get-challenges.ts 读取并记录挑战与提交其中saveSubmissionToDB被显式建模从课程结构看该挑战所在的块配置 curriculum/structure/blocks/build-a-product-landing-page-project.json 声明了helpCategory: HTML-CSS、usesMultifileEditor: true并且该块被编排进响应式网页设计认证的超块顺序中——见 curriculum/structure/superblocks/responsive-web-design-22.json。换句话说这是一道给你自由发挥视觉创意、但行为必须达标的自由项目Project型题目。它与同为该认证的 Survey Form、Tribute Page 等项目并列放在 Flexbox 相册、响应式钢琴、CSS Grid 杂志等课程块之后用于综合检验你对HTML 语义标签 CSS Flexbox 媒体查询Media Query的掌握程度。二、项目目标与完整验收清单15 条用户故事挑战原文给出的目标是构建一个功能上与 freeCodeCamp 官方在线演示文档中的 product-landing-page.freecodecamp.rocks 站点相似的应用但严禁复制其代码与视觉稿。随后给出 15 条可勾选的 User Stories这是整个项目唯一的功能契约逐条列出如下#用户故事要点DOM / CSS 关键词1页面有header带idheader语义化页头2header 内有一张图片idheader-imgLogo 是理想选择品牌图片3#header内有nav带idnav-bar导航容器4nav 内至少有 3 个可点击元素class 为nav-link锚点链接5点击.nav-link可跳到落地页对应区块页内锚点跳转6页面能播放一段嵌入式产品视频idvideovideo或iframe7有一个form带idform邮箱订阅表单8表单内有idemail的输入框可输入邮箱邮箱输入9#email必须有 placeholder 占位文案输入提示10#email使用 HTML5 校验typeemail原生表单校验11表单内有idsubmit的提交按钮提交控件12点击#submit后邮箱提交到静态页mock 地址https://www.freecodecamp.com/email-submit表单 action13导航栏始终固定在视口顶部position: fixedtop14页面至少使用一次媒体查询media15页面至少使用一次 CSS Flexboxdisplay: flex/inline-flex第 34 行的附加说明还强调在 HTML 中必须写link relstylesheet hrefstyles.css否则 CSS 不会生效——这是多文件编辑器自动评测的前提。需求整体呈现出经典产品落地页的信息架构吸顶导航 → 产品卖点Features→ 视频展示 → 定价方案Pricing→ 邮箱收集表单 → 页脚。用户故事第 5 条尤其关键nav-link的锚点目标必须真实存在于页面中这是单页导航能否工作的前提。三、自动化验收测试全解读需求如何变成可执行断言挑战文档的--hints--区段提供了浏览器端自动执行的判定 JS。它们共同勾勒出如何才算通过。理解这些断言等于拿到了阅卷标准下面按四组解读。3.1 DOM 结构断言组header / logo / 导航const el document.getElementById(header) assert(!!el el.tagName HEADER) // 必须是语义化 header const els document.querySelectorAll(#header #header-img) // 必须是 #header 的后代 assert(els.length 0) const el document.getElementById(header-img) assert(!!el !!el.src /^http/.test(el.src)) // src 必须是以 http 开头的合法 URL第一组断言告诉我们仅看起来像是不够的。getElementById配tagName HEADER强制要求标签语义正确logo 图片必须是#header的后代节点不能用兄弟节点并且其src必须是可解析的远程 URL——从测试看仓库官方解决方案里直接使用了维基共享资源的图片链接本地相对路径会导致el.src无法以http开头而挂掉。导航断言组进一步约束层级与链接行为const els document.querySelectorAll(#header #nav-bar) // #nav-bar 须在 #header 内 assert(els.length 0) const els document.querySelectorAll(#nav-bar .nav-link) assert(els.length 3) // 至少 3 个导航项 els.forEach(el { const linkDestination el.getAttribute(href).slice(1) // 取 href 去掉 # if (!document.getElementById(linkDestination)) assert(false) // 锚点目标必须真实存在 })最容易被忽略的是最后一段它不会接受hrefhttps://example.com这类站外链接因为.slice(1)后根本找不到对应 id 的 DOM 节点换言之所有.nav-link必须是指向本页内区块 id 的#xxx锚点。3.2 视频嵌入断言组#videoconst el document.getElementById(video) assert(!!el (el.tagName VIDEO || el.tagName IFRAME)) // 二选一皆可 let el document.getElementById(video) const sourceNode el.children let sourceElement null if (sourceNode.length) { sourceElement [...video.children].filter(el el.localName source)[0] } if (sourceElement) { el sourceElement } // 若用 videosource .../video 需递归检查 assert(el.hasAttribute(src))这里展示了原生video的两种合法写法直接src或用内嵌source子元素测试会先找source再做二次判定。官方参考实现采用的是iframe内嵌 YouTube 播放器这是产品视频最常见的落地方案。3.3 订阅表单断言组#form/#email/#submit这是约束最密集的一组本质是验证一个能提交给静态后端的合法 HTML 表单const el document.getElementById(form) assert(!!el el.tagName FORM) // 必须语义化 form const els document.querySelectorAll(#form #email) assert(els.length 0) // email 是 form 的后代 const el document.getElementById(email) assert(!!el !!el.placeholder el.placeholder.length 0) // placeholder 非空 assert(!!el el.type email) // 必须 typeemail 才触发 HTML5 校验 assert(!!el el.name email) // name 用于提交键名 const el document.getElementById(form) assert(!!el el.action https://www.freecodecamp.com/email-submit) // action 精确匹配 const el document.getElementById(submit) assert(!!el el.type submit) // 按钮必须 typesubmit据此可推导表单的最小合规骨架form idform actionhttps://www.freecodecamp.com/email-submit input idemail nameemail typeemail placeholder请输入邮箱 required / input idsubmit typesubmit value订阅 / /form其中action是全等匹配多一个斜杠、大小写差异都会失败typeemail让浏览器在提交前自动拦截非法格式——这正是用户故事第 10 条HTML5 校验的落点无需任何 JS。3.4 布局与 CSS 能力断言组固定导航 / 媒体查询 / Flexbox这三条是纯 CSS 实力的考验判定的 JS 也更复杂。先看吸顶导航测试——它真实地滚动页面再量取元素位置const timeout (milliseconds) new Promise((resolve) setTimeout(resolve, milliseconds)); const header document.getElementById(header); const headerChildren header.children; const navbarCandidates [header, ...headerChildren]; const getNavbarPosition (candidates []) { return candidates.reduce( (min, candidate) Math.min(min, Math.abs(candidate?.getBoundingClientRect().top)), Infinity ); }; assert.approximately(getNavbarPosition(navbarCandidates), 0, 15, ...); window.scroll(0, 500); // 向下滚 500px await timeout(1); assert.approximately( getNavbarPosition(navbarCandidates), 0, 15, ... even after scrolling ); window.scroll(0, 0);这条断言语义相当精确先要求#header或其某个直接子元素此刻的getBoundingClientRect().top与视口顶部 0 的偏差不超过15px再滚动 500px 后再次要求仍贴顶。它允许header本身position: fixed也允许 header 的某个子元素如实际固定住的 nav吸顶——因此导航永远在视口顶部的标准实现是#nav-bar { position: fixed; /* 脱离文档流、相对视口定位 */ top: 0; /* 贴住视口顶端 */ width: 100%; }媒体查询与 Flexbox 的检测则借助了测试环境中注入的__helpers.CSSHelpconst cssCheck new __helpers.CSSHelp(document).getCSSRules(media) assert(cssCheck.length 0 || htmlSourceAttr.length 0) const hasFlex (rule) [flex, inline-flex].includes(rule.style?.display) const stylesheet new __helpers.CSSHelp(document).getStyleSheet() const cssRules new __helpers.CSSHelp(document).styleSheetToCssRulesArray(stylesheet) const mediaRules new __helpers.CSSHelp(document).getCSSRules(media) const usesFlex cssRules.find(rule hasFlex(rule)) const usesFlexMedia mediaRules.find(mediaRule { return [...mediaRule.cssRules].find(rule hasFlex(rule)) }) assert(usesFlex || usesFlexMedia)值得注意的工程细节有三点媒体查询不仅能写在 CSS 里source media...属性也会被统计对应picture场景Flexbox 必须真的写出来判定是解析 CSSOM 规则检查是否有display: flex或inline-flex藏在 CSS 注释里的不算媒体查询内部的 flex 也被计入检测会递归扫描media规则内部的规则列表避免误判。这也就意味着仅用float、grid或纯文本排版无法过关——必须显式声明 Flexbox 并把某一关键区域典型是导航项横向排列或特性列表交给 flex 容器管理。四、官方参考实现Solution逐段拆解挑战文档末尾附带了一份完整可运行的参考方案主题是一个宝可梦托儿所服务Pokemon Daycare Service。它足够简短、又能覆盖全部 15 条故事非常值得逐段对照阅读。其 HTML 骨架如下与仓库源文档一致结构完整保留!DOCTYPE html html langen head meta charsetUTF-8 / link relstylesheet hrefstyles.css / titleProduct Landing Page/title /head body header idheader nav idnav-bar img idheader-img srchttps://upload.wikimedia.org/wikipedia/commons/3/39/Pokeball.PNG max-height50px / a href#Features classnav-linkFeatures/a | a href#Video classnav-linkSee our facility!/a | a href#Pricing classnav-linkPurchase/a hr / /nav /header main h1Pokemon Daycare Service/h1 section idFeatures h2What we offer/h2 div classflex-here div classflex-left img idbullet srchttps://upload.wikimedia.org/wikipedia/commons/3/39/Pokeball.PNG max-height25px / /div div classflex-rightGuaranteed friendly and loving staff!/div /div !-- 另外两条 feature 行结构相同仅文案不同 -- div classflex-here…Comfortable environment for Pokemon to explore and play!…/div div classflex-here…Multiple membership plans to fit your lifestyle!…/div /section section idVideo h2Check us out!/h2 A sneak peek into our facility: br / iframe idvideo width520 height347 srchttps://www.youtube.com/embed/Nw-ksH2r6AQ frameborder0 allowfullscreen altA video tour of our facility/iframe /section section idPricing h2Membership Plans/h2 div classflex-mem div classflex-mem-boxfont size2Basic Membership/fontbr / ulliOne Pokemon/liliFood and berries provided/li/ul em$9.99/month/em /div !-- Silver / Gold 两栏结构相同价格与权益递增 -- /div /section form idform actionhttps://www.freecodecamp.com/email-submit pSign up for our newsletter!/p label foremail pEmail:/p input nameemail idemail typeemail placeholderjohnsmithemail.com required /label input typesubmit idsubmit /form footer a href../Return to Project List/a | a hrefhttps://www.nhcarrigan.comReturn to HomePage/a /footer /main /body /html它与验收标准一一对应可以做一张映射表#nav-bar被放在#header内部 → 满足故事 1、3三个href#Features/#Video/#Pricing→ 满足故事 4、5#header-img使用维基共享资源上的 http 图片 → 满足故事 2iframe idvideo srchttps://www.youtube.com/embed/Nw-ksH2r6AQ→ 满足故事 6视频用 YouTube 官方嵌入路径form idform actionhttps://www.freecodecamp.com/email-submittypeemail的#email带 placeholder、name、requiredtypesubmit的#submit→ 完整命中故事 712position: fixed; top: 0的#nav-bar、特性区/定价区的display: flex、底部的media→ 命中故事 1315。它的配套 CSS 也全部围绕这几个目标body { background-color: #3a3240; color: white; } main { max-width: 750px; margin: 50px auto; } input { background-color: #92869c; } a:not(.nav-link) { color: white; } #header-img { max-height: 25px; } #nav-bar { position: fixed; /* 吸顶核心脱离文档流 贴视口顶部 */ width: 100%; text-align: center; top: 0%; background-color: #92869c; } h1 { text-align: center; } body { text-align: center; } footer { text-align: center; } #bullet { max-height: 25px; } .flex-here { display: flex; /* 特性列表横向排布 */ justify-content: center; } .flex-left { height: 25px; } .flex-mem { display: flex; /* 三档定价卡片并排 */ justify-content: center; } .flex-mem-box { background-color: #92869c; border-color: black; border-width: 5px; border-style: solid; margin: 10px; padding: 10px; color: black; } media (max-width: 350px) { /* 小屏下缩小视频 */ #video { width: 300; height: 200; } }这套实现里弹性和可扩展的杠杆点非常明确导航项、特性行、定价卡片都被抽象成 flex 容器或重复类名.flex-here、.flex-mem、.flex-mem-box新增一条卖点或一档套餐只需要复制一个结构相同的节点即可页面主体max-width: 750px; margin: 50px auto让它在大屏居中、窄屏自适应。当然参考实现为了同时兼容老式写法用了font、未闭合的max-height属性等本文写作时建议你在自己的项目里用现代语义标签替换如ul导航、规范 width 单位这也符合原文档Give it your own personal style的号召。五、从能过测到做好看实践路线与进阶要点通过阅读以上测试语义与参考实现一份稳健的完成路径可以这样设计先搭 HTML 语义骨架header nav#nav-bar .nav-link*3、section#Features / #Video / #Pricing、底部form#form保证所有 id 与锚点严格对应先用占位内容跑通全部 DOM 断言补视频与表单YouTube 嵌入 iframe 注意给足idvideo与src表单四个要素form 的 action、email 的 type/name/placeholder、submit 的 type一次性写齐避免逐条返工再上 CSS 三件套用position: fixedtop: 0固定导航、至少一个display: flex容器、一个media断点随后再做视觉打磨用真实内容替换占位官方演示只要求功能相似Logo、配色、文案与配图均可自由发挥建议加入产品专属的品牌色与卡片阴影让布局更接近现代 SaaS 落地页。想进一步扩展技能可以在本仓库找到配套的先修课程块learn-html-forms-by-building-a-registration-form表单与 HTML5 校验、learn-css-flexbox-by-building-a-photo-galleryFlexbox 实战与learn-responsive-web-design-by-building-a-piano媒体查询与响应式它们的挑战源文件同位于 curriculum/challenges/english/blocks 下可作为体系化参考。结语Build a Product Landing Page是 freeCodeCamp 响应式网页设计认证中少有的零脚手架自由创作项目文档给出的 15 条用户故事定义了功能下限而 20 组浏览器内断言则把下限翻译成了可执行的 DOM/CSS 规则——吸顶导航、页内锚点、HTML5 表单校验、Flexbox 与媒体查询缺一不可。沿着先结构、后样式、再打磨的顺序参考 挑战源文档 与仓库内的块配置 build-a-product-landing-page-project.json你完全可以交付一个既全部通过测试、又独具个人风格的产品页面。【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询