Build Challenge: Accessible Portfolio Page
Your take-home test for a frontend role. Ship a responsive, accessible portfolio that passes a Lighthouse audit — the way a real hiring team will evaluate you.
Why this matters at your level
This is your portfolio and your screening test simultaneously. The Lighthouse scores will be run on your actual deployed site during interviews. Polish this until all four hit 90+. It becomes the artifact you point to for the next 2 years.
At mid level, perfect Lighthouse scores are table stakes. The differentiation is architectural: why did you make specific HTML structure choices, how do you handle font loading strategy, and how would you scale this to a multi-page site with shared components.
Build Challenge: Accessible Portfolio Page
Your take-home test for a frontend role. Ship a responsive, accessible portfolio that passes a Lighthouse audit — the way a real hiring team will evaluate you.
You receive the brief: build a portfolio page. Figma link, 48-hour deadline, Lighthouse will be run against it.
You scaffold HTML -- but you used div for everything. Lighthouse accessibility score: 43.
CRITICALSemantic HTML, alt text, ARIA landmarks added. Accessibility score: 100. Performance still 61 -- unoptimized hero image.
WARNINGImage compressed to WebP, lazy-loaded below fold, font preloaded. All four scores >= 90. Ready to submit.
The question this raises
If a machine can disqualify you before a human reads your code, what does "good HTML and CSS" actually mean in production?
A candidate submits a portfolio that looks perfect in Chrome on a 1440px monitor. Lighthouse runs and returns: Performance 94, Accessibility 67, Best Practices 92, SEO 88. What is most likely causing the accessibility score?
Lesson outline
Before you write a line: the brief
You are applying for a frontend role. The take-home challenge is: build a personal portfolio page. No framework required. The hiring team will run Lighthouse against your deployed URL and look at your HTML source before they look at your JavaScript.
What the hiring team actually evaluates (in order)
- 1. Lighthouse scores — Automated, ruthless. Performance >= 90, Accessibility = 100, Best Practices >= 90, SEO >= 90. Fail one and the review stops.
- 2. HTML source — Is there one h1? Are nav, main, footer used? Do images have meaningful alt text? Is the title tag unique and descriptive?
- 3. CSS architecture — Is there a clear layout system (Flexbox or Grid)? Are there magic numbers and !important hacks? Does it break at 375px?
- 4. Keyboard navigation — Tab through the page. Can you reach every link and button? Is focus visible? Does a modal (if any) trap focus correctly?
- 5. Code review — Is the HTML semantic? Is the CSS organised? Are there obvious performance issues (render-blocking fonts, oversized images)?
The naive approach: what gets candidates rejected
Most rejections come from the same 4 mistakes. They are invisible in Chrome but glow in Lighthouse.
1<!-- Mistake 1: div soup — screen reader reads "generic, generic, generic" -->2<div class="header">No landmark role — nav element missing3<div class="name">Jane Smith</div>4<div class="nav">div with onclick: not keyboard accessible, no ARIA role5<div onclick="scroll('about')">About</div>6<div onclick="scroll('work')">Work</div>7</div>8</div>910<!-- Mistake 2: missing alt text — Lighthouse deducts points per image -->11<img src="hero.png">Missing alt="" — Lighthouse Accessibility -5 pts per image12<img src="project1.jpg">1314<!-- Mistake 3: render-blocking Google Fonts -->Render-blocking stylesheet — LCP penalty15<link href="https://fonts.googleapis.com/css2?family=Inter" rel="stylesheet">1617<!-- Mistake 4: fixed-pixel layout — breaks on 375px iPhone -->18<div style="width: 960px; margin: 0 auto;">
div-as-nav
<div class="nav">
<div onclick="goto('about')">About</div>
</div><nav aria-label="Main navigation">
<a href="#about">About</a>
</nav>A div with onclick is not keyboard accessible and has no semantic role. A nav with anchor tags is free keyboard navigation, ARIA landmark, and crawlable link — zero extra code.
What Lighthouse shows you
Run Lighthouse in Chrome DevTools (Lighthouse tab → Analyze page load) before you submit. Every red item is a deduction. Here is what a failing submission looks like:
Typical failing Lighthouse report
Performance: 61 — "Properly size images" (hero.png served at 4.2MB, displayed at 600px wide) Accessibility: 43 — "Image elements do not have [alt] attributes" (6 images), "Buttons do not have an accessible name" (3 divs with onclick) Best Practices: 78 — "Serves vulnerable JavaScript libraries" (jQuery 1.6 from CDN) SEO: 52 — "Document does not have a meta description", "Links do not have descriptive text" (3x "click here")
Each item in the report links directly to the failing element and the fix. Lighthouse is not a mystery — it is a checklist with line-item feedback.
Build it: the acceptance criteria
Ship a portfolio page that meets all of the following. A senior engineer will verify each item in your code review.
| Criterion | How to verify | Common failure mode |
|---|---|---|
| Single h1 with your name/title | Ctrl+F "h1" in source — should appear once | Multiple h1s, or h1 inside a div inside another div |
| nav, main, footer landmarks | Accessibility pane in DevTools → Landmarks | Using div.header, div.main, div.footer instead |
| All images have descriptive alt text | Lighthouse Accessibility audit | alt="" on meaningful images, or missing entirely |
| Fully keyboard navigable | Tab through page, no mouse | onclick on divs, missing focus styles (outline: none) |
| Mobile layout at 375px | DevTools Device toolbar → iPhone SE | Fixed-pixel widths, horizontal scroll, overlapping text |
| Lighthouse >= 90 all four scores | DevTools Lighthouse tab | Unoptimised images, render-blocking fonts, missing meta |
| No !important in CSS | Search for !important in stylesheet | Specificity wars from non-semantic class names |
The 10-minute pre-submission checklist
1. View source — count h1 (must be 1). 2. Tab through from the top — every interactive element reachable. 3. Resize to 375px — no horizontal scroll. 4. Lighthouse in incognito — all four scores on screen. 5. Grep for !important — must return 0. Ship it.
How to talk about this project in your interview
The live code review comes after the take-home. The interviewer will open your source and ask questions designed to separate people who Googled the fix from people who understand it.
Questions you will be asked — and what the strong answer sounds like
- Why did you use nav here instead of a div? — Strong: "nav gives screen readers a landmark they can jump to with a keyboard shortcut, and it tells search engines these links are navigation — not content." Weak: "because nav is more semantic."
- Your LCP is 1.2s — what did you do to get there? — Strong: "I converted the hero to WebP (60% smaller), added width/height to prevent layout shift, and preloaded it with a link rel=preload tag so the browser fetches it in parallel with CSS." Weak: "I compressed the image."
- How did you handle focus management? — Strong: "Every interactive element is a native a or button so browser focus is free. I only added tabindex="-1" on the skip-link target so focus can land there programmatically." Weak: "I just left the default."
- What would you change if this needed to scale to 50 pages? — Strong: "I would extract the layout into CSS custom properties for tokens, move to a static site generator so I am not duplicating the nav HTML, and add a sitemap.xml for SEO." Weak: "I would add more CSS."
1<!-- The version that passes every check -->2<!DOCTYPE html>3<html lang="en">4<head>5<meta charset="UTF-8">6<meta name="viewport" content="width=device-width, initial-scale=1.0">7<meta name="description" content="Jane Smith — Frontend Developer based in Berlin">Meta description — required for SEO score8<title>Jane Smith | Frontend Developer</title>9<!-- Preload hero image — LCP element -->Preload hero: tells browser to fetch it immediately, improves LCP10<link rel="preload" as="image" href="hero.webp">11<!-- Self-host font or use font-display: swap to avoid render-block -->12<link rel="preconnect" href="https://fonts.googleapis.com">13</head>14<body>15<a href="#main" class="skip-link">Skip to main content</a>Skip link: keyboard users jump to content, required for a11y = 1001617<header>18<h1>Jane Smith</h1>19<nav aria-label="Main navigation">20<ul>21<li><a href="#about">About</a></li>22<li><a href="#work">Work</a></li>23<li><a href="#contact">Contact</a></li>24</ul>25</nav>26</header>2728<main id="main">29<section aria-labelledby="about-heading">30<h2 id="about-heading">About</h2>31<imgwidth + height on img: browser reserves space, eliminates CLS32src="hero.webp"33alt="Jane Smith presenting at JSConf Berlin 2023"34width="1200"35height="630"36>37</section>38</main>3940<footer>41<p><small>© 2024 Jane Smith</small></p>42</footer>43</body>44</html>
Exam Answer vs. Production Reality
Semantic HTML
📖 What the exam expects
Using elements like nav, header, main, footer, article, section that describe the meaning of content, not just its appearance.
Toggle between what certifications teach and what production actually requires
How this might come up in interviews
Take-home challenges, portfolio reviews, and "walk me through a project you built" questions all land on this material. The interviewer has your source open while you talk.
Common questions:
- Walk me through your HTML structure — why did you organise it this way?
- Your Lighthouse performance is 94 — what would you do to push it to 99?
- How did you ensure keyboard accessibility?
- What happens to your layout at 320px? At 1440px?
- If a designer asked you to add a modal to this page, what accessibility considerations would you add?
Strong answer: Preloaded hero image. Skip link. Width/height on images. Explains the LCP strategy unprompted. Has already considered how to extend this to multiple pages.
Red flags: Lighthouse accessibility below 90. Fixed pixel layouts. Missing meta description. div soup with onclick handlers. Cannot explain a single architectural choice.
Ready to see how this works in the cloud?
Switch to Career Paths for structured paths (e.g. Developer, DevOps) and provider-specific lessons.
View role-based pathsSign in to track your progress and mark lessons complete.
Discussion
Questions? Discuss in the community or start a thread below.
Join DiscordIn-app Q&A
Sign in to start or join a thread.