ποΈ Weekend Project: HTML
Everything you learned in Module 3 comes together this weekend into one thing you can point at and say, "I built that." You'll ship a five-page website for a business of your choosing β structured with semantic HTML, wired together with real navigation, and accessible from the first keystroke. No CSS, no JavaScript yet. Just a clean, correct skeleton that the rest of the course will dress up.
π― Learning Objectives
By the end of this build, you will be able to:
- Plan a multi-page site with a sitemap and a shared page template before writing markup
- Build five interconnected pages using semantic landmarks (
<header>,<nav>,<main>,<footer>) and a logical heading hierarchy - Compose an accessible contact form and a properly structured data table
- Embed media (image, video, or audio) with captions and text alternatives
- Validate your markup and audit it against a keyboard- and screen-reader-friendly checklist
Estimated Time: 4β8 hours over a weekend β’ Difficulty: BeginnerβIntermediate
Hands-on: This whole lesson is the exercise β a guided build with milestones you check off as you go.
In This Lesson
The Brief
Pick a fictional business or organization you find fun to build for β a neighborhood coffee roaster, a rock-climbing gym, an indie board-game studio, a community garden. The topic barely matters; the structure is what you're being graded on. Your job is to produce a small but complete website: five pages that link to each other, all built from hand-written, standards-clean HTML.
π‘ Think of it as framing a house. Right now you're nailing up studs and joists, not choosing paint. A crooked frame ruins everything that follows, but a square, well-braced one makes the finish work easy. CSS (Module 4) is the paint; JavaScript (later) is the wiring. This weekend is the frame β get it plumb.
π The five pages you'll deliver
index.html β home / landing page that introduces the business.
about.html β the story, the team, the mission.
services.html β what you offer, including a pricing table.
contact.html β an accessible contact form and address details.
gallery.html (or blog / testimonials) β your choice, and the home for your embedded media.
β οΈ The one rule that trips people up
No CSS this weekend, and no JavaScript. It is genuinely tempting to reach for a framework or drop in a stylesheet to make it "look done." Resist. An unstyled page that is semantically perfect is exactly what this milestone asks for β and it will look great the moment you add CSS next module. A pretty page built from <div> soup is a step backward.
Milestone 0 β Plan & Sitemap
Ten minutes of planning saves an hour of rework. Before you open your editor, sketch how your pages connect. Every page links to every other page through a shared navigation menu, and the home page is the hub everyone can return to.
Home"] --> B["about.html
About"] A --> C["services.html
Services"] A --> D["contact.html
Contact"] A --> E["gallery.html
Gallery"] B --> A C --> A D --> A E --> A
Now set up your folder. A flat structure is perfect for a five-page static site β keep assets in an images/ subfolder so the root stays readable:
my-site/
βββ index.html
βββ about.html
βββ services.html
βββ contact.html
βββ gallery.html
βββ images/
βββ logo.png
βββ hero.jpg
π‘ One decision to make now
Write down your business name, a one-line tagline, and the five nav labels on paper. You'll type these identical strings into all five pages β deciding them once keeps your navigation consistent and stops the "was it 'About' or 'About Us'?" drift that breaks the shared-template illusion.
Milestone 1 β The Shared Page Template
Every one of your five pages shares the same outer shell: a skip link, a header with navigation, a <main> landmark, and a footer. Build this once, get it perfect, then copy it into each file and swap only the <main> contents and the <title>. This is exactly how real templating engines work under the hood β you're just doing it by hand.
Here is the skeleton. Notice the four semantic landmarks and the skip link as the very first focusable element:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Home | Roast & Ramble Coffee</title>
<meta name="description" content="Small-batch coffee roasted daily in the old mill district.">
</head>
<body>
<a href="#main-content" class="skip-link">Skip to main content</a>
<header>
<p class="logo">Roast & Ramble</p>
<nav aria-label="Main">
<ul>
<li><a href="index.html" aria-current="page">Home</a></li>
<li><a href="about.html">About</a></li>
<li><a href="services.html">Menu</a></li>
<li><a href="gallery.html">Gallery</a></li>
<li><a href="contact.html">Contact</a></li>
</ul>
</nav>
</header>
<main id="main-content">
<!-- The ONLY part that changes per page -->
</main>
<footer>
<p>© 2026 Roast & Ramble Coffee. All rights reserved.</p>
</footer>
</body>
</html>
β Three details that make this template pull its weight
<html lang="en">β tells screen readers which language to pronounce. Non-negotiable.- The skip link is first β a keyboard user can jump straight past the repeated menu into the page's unique content.
aria-current="page"β set this on the current page's own nav link (and remove it from the others) so assistive tech announces "you are here."
Why not just use<div class="nav">? Because<nav>,<header>,<main>, and<footer>are landmarks. Screen-reader users press a single key to jump between them. A wall of<div>s gives them nothing to navigate by β the page becomes one undifferentiated blob.
Milestone 2 β Build the Five Pages
With the template in hand, fill in each <main>. The golden rule for content: exactly one <h1> per page (the page's title), and never skip heading levels β an <h2> may be followed by an <h3>, but not straight to an <h4>.
Home page β the hook
Lead with who you are and why a visitor should care, then a few highlights:
<section aria-labelledby="welcome">
<h1 id="welcome">Coffee worth the walk</h1>
<p>Small-batch beans, roasted every morning in the old mill district.</p>
<img src="images/hero.jpg"
alt="Barista pouring a latte with a leaf pattern in the foam">
</section>
<section aria-labelledby="highlights">
<h2 id="highlights">Why people come back</h2>
<article>
<h3>Roasted daily</h3>
<p>Beans go from roaster to cup within 24 hours.</p>
</article>
<article>
<h3>Fair sourcing</h3>
<p>We buy direct from growers we've visited ourselves.</p>
</article>
</section>
π <section> vs <article>
A <section> is a thematic grouping within a page (the "Highlights" area). An <article> is a self-contained unit that would still make sense lifted out on its own (a single highlight card, a blog post, a product). Give each <section> a heading and tie it together with aria-labelledby pointing at that heading's id.
About, Services & Gallery pages
Each follows the same pattern: a single <h1>, then <section>s with <h2>s. The About page tells the story and introduces the team; the Services page lists what you offer and carries your pricing table (Milestone 4); the Gallery page hosts your embedded media. Reuse structures you already know β an image with a caption belongs in a <figure>:
<figure>
<img src="images/mill-1998.jpg"
alt="The mill building the year the roastery opened">
<figcaption>Our first roaster, installed in 1998.</figcaption>
</figure>
Milestone 3 β The Contact Form
The contact page is where accessibility really shows. The single most important rule: every input has a real <label>, connected by matching for and id. A placeholder is not a label β it vanishes the moment someone types.
<h1>Contact us</h1>
<address>
12 Mill Lane, Riverside<br>
<a href="tel:+15551234567">(555) 123-4567</a><br>
<a href="mailto:hello@roastramble.test">hello@roastramble.test</a>
</address>
<form action="#" method="post">
<p>
<label for="name">Your name</label>
<input type="text" id="name" name="name" required>
</p>
<p>
<label for="email">Email address</label>
<input type="email" id="email" name="email" required>
</p>
<fieldset>
<legend>Preferred contact method</legend>
<p>
<input type="radio" id="by-email" name="contact-method" value="email">
<label for="by-email">Email</label>
</p>
<p>
<input type="radio" id="by-phone" name="contact-method" value="phone">
<label for="by-phone">Phone</label>
</p>
</fieldset>
<p>
<label for="message">Your message</label>
<textarea id="message" name="message" rows="5" required></textarea>
</p>
<button type="submit">Send message</button>
</form>
β Form checklist for this milestone
- Every
<input>,<textarea>, and<select>has a matched<label>. - Radio buttons and checkboxes that belong together are wrapped in a
<fieldset>with a<legend>. - Use the right
typeβtype="email",type="tel"β so browsers validate and mobile keyboards adapt. requiredalone marks a field mandatory; you don't also needaria-requiredin modern HTML.
β οΈ action="#" is a placeholder. With no backend yet, the form won't actually send anything, and that's fine for this weekend β you're proving the structure is right. Wiring it to a server is a later-module job.
Milestone 4 β Tables & Media
A real data table (Services page)
Tables are for tabular data β never for layout. A correct table has a <caption>, a <thead> with <th scope="col">, and row headers with <th scope="row"> so screen readers can announce "Premium, Price, $6" instead of a naked "$6."
<table>
<caption>Bag pricing (250 g)</caption>
<thead>
<tr>
<th scope="col">Blend</th>
<th scope="col">Roast</th>
<th scope="col">Price</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">House</th>
<td>Medium</td>
<td>$14</td>
</tr>
<tr>
<th scope="row">Single origin</th>
<td>Light</td>
<td>$18</td>
</tr>
</tbody>
</table>
Embedded media with a text alternative (Gallery page)
Native <video> and <audio> give you free controls. Always provide a <track> of captions for video and fallback content for browsers that can't play the file:
<video controls width="640" poster="images/video-thumb.jpg">
<source src="media/roast-day.mp4" type="video/mp4">
<source src="media/roast-day.webm" type="video/webm">
<track kind="captions" src="media/roast-day.vtt"
srclang="en" label="English">
<p>Your browser can't play this video.
<a href="media/roast-day.mp4">Download it instead</a>.</p>
</video>
π‘ No media files handy?
You don't need a real video to satisfy this milestone. A single well-captioned <figure> image, or a short public-domain audio clip with a <details> transcript, counts. What's being assessed is that you used the right element with the right text alternative β not the production value of the clip.
Milestone 5 β Accessibility Pass
Now walk the whole site once more, this time wearing an accessibility hat. The build flows through these stages β and this final pass is the one most beginners skip and most reviewers notice first:
Do these three concrete tests β they catch the overwhelming majority of real problems:
- Unplug your mouse. Press Tab through every page. Can you reach and activate every link and form control? Does the skip link appear first? If focus gets lost or trapped, fix it.
- Validate the markup. Paste each page into the W3C Nu HTML Checker. Aim for zero errors β unclosed tags and duplicate
ids hide real bugs. - Audit with WAVE. Run each page through the WAVE tool and clear every red error (missing alt text, empty links, unlabeled inputs).
β οΈ The four errors reviewers see most
- Skipped heading levels β jumping
<h1>straight to<h3>. Keep the ladder unbroken. - Missing
alttext β every informative image needs a description; decorative images getalt=""(empty, but present). - Inputs without labels β a placeholder is not a label.
- "Click here" links β link text should describe its destination out of context: "Read our sourcing story", not "click here".
Definition of Done
Your project is finished when you can honestly tick every box below. Print this or copy it into a note and check it off page by page.
π Structure
- β Five pages exist and all link to each other through a shared
<nav> - β Every page starts with
<!DOCTYPE html>and<html lang="en"> - β Each page uses
<header>,<nav>,<main>, and<footer>landmarks - β Each page has exactly one
<h1>and no skipped heading levels - β Each page has a unique, descriptive
<title>andmeta description
π Content & components
- β At least one accessible
<table>with<caption>and scoped headers - β A contact
<form>where every control has a matched<label> - β Embedded media (image, video, or audio) with a text alternative
- β A mix of lists, links, and at least one
<figure>/<figcaption>
π Accessibility & quality
- β A working "Skip to main content" link is the first focusable element
- β Every informative image has meaningful
alttext; decorative ones havealt="" - β Every page passes the W3C validator with zero errors
- β The whole site is fully operable with the keyboard alone
What Good Looks Like
"Done" and "good" aren't the same bar. Here's how to tell a merely-complete submission from a genuinely strong one β the difference is almost always consistency and correctness, not more features.
| Dimension | Needs work | Good | Excellent |
|---|---|---|---|
| Semantics | Mostly <div>s with class names |
Correct landmarks on every page | Landmarks plus <article>/<figure>/<time> used precisely |
| Consistency | Nav differs from page to page | Identical header/footer everywhere | Shared template plus correct aria-current per page |
| Forms | Placeholders instead of labels | Every control labelled | Fieldsets, legends, and correct input types throughout |
| Accessibility | Validator and WAVE errors remain | Zero validator errors, keyboard-navigable | Clean WAVE audit and a considered heading outline |
β The tell of an excellent submission
Open the site with your eyes closed β that is, turn off styling entirely (which it already is) and read the page top to bottom as plain text. If the heading outline alone tells you exactly what each page is about and where you are in the site, you've nailed it. That linear, logical read is precisely what a screen-reader user experiences, and it's what all the fundamentals in Module 3 were building toward.
Summary & Quiz
π Key Takeaways
- Plan first: a sitemap and a shared template keep five pages consistent.
- Semantic landmarks (
header/nav/main/footer) and a clean heading ladder are the whole point of this build. - Accessible forms and tables come down to labels, fieldsets, captions, and scoped headers.
- The final accessibility pass β keyboard test, validator, WAVE β is what separates "good" from "done."
π― Quick Quiz
Question 1: Why should the "Skip to main content" link be the first focusable element on the page?
Question 2: A form field shows the text "Your name" only inside the box as a placeholder, with no <label>. What's the problem?
Question 3: Which change best signals an excellent rather than merely complete project?
π Further Reading
- MDN β HTML element reference
- W3C WAI β Web accessibility tutorials
- WebAIM β Semantic structure
- The A11Y Project β Accessibility checklist
π What's Next?
You've built a complete, accessible HTML skeleton β the hardest habits in web development, learned first. Next module opens with forms in depth, starting with Form Structure and Attributes, where you'll take the contact form you just built and make it genuinely robust.
π That's a real website!
Five pages, hand-built, standards-clean, and accessible from the first tab press. Everything that follows just makes this frame beautiful.