Overview
A blog homepage is one of the clearest real-world cases for semantic sectioning elements, because a real blog homepage genuinely has multiple, independent kinds of content on one page: a masthead, a featured story, a list of recent posts, and supporting information off to the side. Each of those deserves its own landmark, and HTML5 has a specific element for almost every one of them.
This tutorial builds that homepage using `<header>`, `<main>`, `<article>`, `<aside>`, and `<footer>`, adds a genuine data table for post statistics (not a table used for layout — that old practice is exactly what semantic HTML replaced), and embeds a multimedia highlight so the page isn't purely text.
- A site `<header>` with the blog name and primary navigation.
- A featured `<article>` with a hero image, headline, and excerpt.
- A list of recent posts, each its own `<article>`, inside a labeled `<section>`.
- A real `<table>` of post statistics — title, author, published date, and read time.
- A multimedia highlight using an embedded `<video>` element.
- An `<aside>` sidebar for categories and a closing `<footer>`.
Prerequisites
- Semantic landmark elements — header, main, section, article, footer (built in the Portfolio project).
- No prior table knowledge needed — this project introduces `table`, `thead`, `tbody`, `tr`, `th`, and `td` from scratch.
- How `<img>` and its `src`/`alt` attributes work.
- General attribute syntax, since `<video>` introduces a couple of new ones (`controls`, `poster`).
Project Structure
The page is one file, `blog.html`, with three top-level regions inside `<body>`: `<header>` (site name and nav), `<main>` (the featured article, the recent-posts list, and the stats table), and `<footer>`. An `<aside>` sits alongside `<main>` as a sibling, holding content that is related to the blog but not part of its primary content stream — exactly what a sidebar of categories is.
The recent posts are wrapped in a `<section id="recent">` containing several `<article>` elements, one per post — each post genuinely stands alone (it has its own title, author, and content), which is the textbook case for `<article>`. The stats table, by contrast, uses `<table>` because its data has one property nothing else on the page does: rows and columns where each cell's meaning depends on both its row and column header simultaneously. That is what a table is for, and it is the reason tables were pulled out of general page-layout duty once CSS could handle layout instead.
Step 1: Scaffold the Page and Site Header
Start with the standard boilerplate and the site header — a blog name and a short primary navigation, both landmark content for the whole page rather than tied to one post.
<!doctype html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>The Weekly Byte - A Web Development Blog</title> <meta name="description" content="Weekly articles on frontend development, accessibility, and web performance."></head><body>
<!-- Site-wide banner: the blog's name and top-level nav, not tied to any one post --> <header> <h1>The Weekly Byte</h1> <nav> <ul> <li><a href="#featured">Featured</a></li> <li><a href="#recent">Recent Posts</a></li> <li><a href="#stats">Post Stats</a></li> </ul> </nav> </header>
<!-- <main> content and the <aside> sidebar are added in the steps that follow -->
</body></html>Step 2: Build the Featured Article
The featured article is the first thing inside `<main>`. It's wrapped in `<article>` rather than `<section>` because it is one specific, independently meaningful post — it would still make complete sense shared on its own as a link preview. The publish date uses `<time datetime="...">` so both humans and machines (search engines, browsers) can parse it unambiguously, regardless of how it's displayed.
<!-- id="featured" is the target of the header's "Featured" nav link --><article id="featured"> <header> <!-- A nested <header> here scopes to this one article, not the whole page - it groups this post's own title/byline, distinct from the site-wide <header> above --> <h2>Why Semantic HTML Still Matters in 2026</h2> <p>By Priya Nandan — <time datetime="2026-08-01">August 1, 2026</time></p> <!-- datetime="2026-08-01" is the machine-readable date; the visible text can stay human-friendly --> </header> <img src="https://placehold.co/640x320" alt="Illustration of HTML tags forming a webpage structure"> <p> Div-and-span soup still works, technically — browsers don't refuse to render it. But every div you swap for a header, article, or nav is a small gift to search engines, screen readers, and the next developer who opens this file. </p></article>Step 3: List Recent Posts
Recent posts follow, grouped in a `<section>` since the whole block shares one theme ("recent posts") even though each post inside it also gets wrapped in its own `<article>`. Add this section directly after the featured article, still inside `<main>`.
<!-- id="recent" is the target of the header's "Recent Posts" nav link --><section id="recent"> <h2>Recent Posts</h2>
<!-- Each post is its own <article>: standalone, independently meaningful content --> <article> <h3>Flexbox vs. Grid: When to Reach for Which</h3> <p>By Rohan Mehta — <time datetime="2026-07-28">July 28, 2026</time></p> <p>A practical comparison of the two layout models, with real examples of each.</p> <a href="#">Read more →</a> </article>
<article> <h3>A Beginner's Guide to ARIA Landmarks</h3> <p>By Asha Verma — <time datetime="2026-07-21">July 21, 2026</time></p> <p>How ARIA roles fill the gaps that native HTML elements don't cover.</p> <a href="#">Read more →</a> </article>
<article> <h3>Speeding Up Page Loads With Lazy-Loaded Images</h3> <p>By Karan Singh — <time datetime="2026-07-14">July 14, 2026</time></p> <p>The loading="lazy" attribute, explained with before-and-after load times.</p> <a href="#">Read more →</a> </article></section>Step 4: Add a Post Stats Table
A table is the right choice for the post statistics block below, because each cell's meaning depends on both its row (which post) and column (which metric) — that two-axis relationship is exactly what `<table>`, with proper `<th>` headers, communicates to both sighted users and screen readers. `<thead>` separates the header row from the data rows in `<tbody>`, and `scope="col"` tells assistive tech that each `<th>` is a column header, not just bold text.
<!-- id="stats" is the target of the header's "Post Stats" nav link --><section id="stats"> <h2>Post Stats</h2> <table> <!-- <thead> holds the column headers, kept separate from the data rows in <tbody> --> <thead> <tr> <th scope="col">Title</th> <!-- scope="col" tells screen readers this header applies to the whole column below it --> <th scope="col">Author</th> <th scope="col">Published</th> <th scope="col">Read Time</th> </tr> </thead> <tbody> <tr> <td>Why Semantic HTML Still Matters in 2026</td> <td>Priya Nandan</td> <td>Aug 1, 2026</td> <td>6 min</td> </tr> <tr> <td>Flexbox vs. Grid: When to Reach for Which</td> <td>Rohan Mehta</td> <td>Jul 28, 2026</td> <td>8 min</td> </tr> <tr> <td>A Beginner's Guide to ARIA Landmarks</td> <td>Asha Verma</td> <td>Jul 21, 2026</td> <td>5 min</td> </tr> </tbody> </table></section>Step 5: Embed a Multimedia Highlight
The multimedia highlight uses `<video>` with the `controls` attribute, which tells the browser to show its built-in play/pause/volume UI rather than requiring custom JavaScript controls. The `poster` attribute supplies an image shown before playback starts — useful here since no actual video file is attached, so the poster is effectively what displays.
<section id="video-highlight"> <h2>This Week's Highlight</h2> <!-- controls shows the browser's built-in play/pause/volume UI - no custom JS needed --> <!-- poster is the image shown before playback starts (and here, since no <source> is attached, effectively the only thing shown) --> <video controls poster="https://placehold.co/640x360" width="640" height="360"> <!-- A real deployment would add: <source src="highlight.mp4" type="video/mp4"> --> Your browser does not support embedded video. <!-- Fallback text, shown only if the browser can't render <video> at all --> </video></section>Step 6: Add a Sidebar and Footer
Last, the sidebar and footer. `<aside>` is the correct element for the categories list because it is tangentially related content, not part of the main article flow — removing it wouldn't break the meaning of any post. `<aside>` sits next to `<main>` as a sibling, not inside it.
<!-- <aside> for content that's related but not part of the primary article flow; sits as a sibling of <main>, not nested inside it --><aside> <h2>Categories</h2> <ul> <li><a href="#">HTML & CSS</a></li> <li><a href="#">JavaScript</a></li> <li><a href="#">Accessibility</a></li> <li><a href="#">Performance</a></li> </ul></aside>
<!-- Page-wide closing content, so it's a sibling of <header> and <main>, not nested inside them --><footer> <p>© 2026 The Weekly Byte. All rights reserved.</p></footer>Complete Code
Here is the full homepage assembled in order, plus a small stylesheet for the header, table, video, and sidebar.
<!doctype html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>The Weekly Byte - A Web Development Blog</title> <meta name="description" content="Weekly articles on frontend development, accessibility, and web performance."> <style> * { box-sizing: border-box; } body { font-family: Arial, Helvetica, sans-serif; margin: 0; color: #222; line-height: 1.6; background: #fafafa; } header { background: #2b3a55; color: #fff; padding: 20px; text-align: center; } header nav ul { list-style: none; display: flex; justify-content: center; gap: 16px; padding: 0; margin: 10px 0 0; flex-wrap: wrap; } header nav a { color: #fff; text-decoration: none; } main, aside, footer { max-width: 720px; margin: 0 auto; padding: 20px; } article { background: #fff; border: 1px solid #ddd; border-radius: 6px; padding: 16px; margin-bottom: 20px; } article img { max-width: 100%; height: auto; border-radius: 4px; } table { width: 100%; border-collapse: collapse; margin-top: 10px; } th, td { border: 1px solid #ddd; padding: 8px 10px; text-align: left; } th { background: #2b3a55; color: #fff; } video { max-width: 100%; height: auto; border-radius: 6px; display: block; margin-top: 10px; } aside { background: #fff; border: 1px solid #ddd; border-radius: 6px; } aside ul { list-style: none; padding: 0; } aside li { margin-bottom: 8px; } footer { text-align: center; background: #2b3a55; color: #d7dce6; max-width: 100%; } </style></head><body>
<header> <h1>The Weekly Byte</h1> <nav> <ul> <li><a href="#featured">Featured</a></li> <li><a href="#recent">Recent Posts</a></li> <li><a href="#stats">Post Stats</a></li> </ul> </nav> </header>
<main> <article id="featured"> <header> <h2>Why Semantic HTML Still Matters in 2026</h2> <p>By Priya Nandan — <time datetime="2026-08-01">August 1, 2026</time></p> </header> <img src="https://placehold.co/640x320" alt="Illustration of HTML tags forming a webpage structure"> <p> Div-and-span soup still works, technically — browsers don't refuse to render it. But every div you swap for a header, article, or nav is a small gift to search engines, screen readers, and the next developer who opens this file. </p> </article>
<section id="recent"> <h2>Recent Posts</h2> <article> <h3>Flexbox vs. Grid: When to Reach for Which</h3> <p>By Rohan Mehta — <time datetime="2026-07-28">July 28, 2026</time></p> <p>A practical comparison of the two layout models, with real examples of each.</p> <a href="#">Read more →</a> </article> <article> <h3>A Beginner's Guide to ARIA Landmarks</h3> <p>By Asha Verma — <time datetime="2026-07-21">July 21, 2026</time></p> <p>How ARIA roles fill the gaps that native HTML elements don't cover.</p> <a href="#">Read more →</a> </article> <article> <h3>Speeding Up Page Loads With Lazy-Loaded Images</h3> <p>By Karan Singh — <time datetime="2026-07-14">July 14, 2026</time></p> <p>The loading="lazy" attribute, explained with before-and-after load times.</p> <a href="#">Read more →</a> </article> </section>
<section id="stats"> <h2>Post Stats</h2> <table> <thead> <tr> <th scope="col">Title</th> <th scope="col">Author</th> <th scope="col">Published</th> <th scope="col">Read Time</th> </tr> </thead> <tbody> <tr> <td>Why Semantic HTML Still Matters in 2026</td> <td>Priya Nandan</td> <td>Aug 1, 2026</td> <td>6 min</td> </tr> <tr> <td>Flexbox vs. Grid: When to Reach for Which</td> <td>Rohan Mehta</td> <td>Jul 28, 2026</td> <td>8 min</td> </tr> <tr> <td>A Beginner's Guide to ARIA Landmarks</td> <td>Asha Verma</td> <td>Jul 21, 2026</td> <td>5 min</td> </tr> </tbody> </table> </section>
<section id="video-highlight"> <h2>This Week's Highlight</h2> <video controls poster="https://placehold.co/640x360" width="640" height="360"> Your browser does not support embedded video. </video> </section> </main>
<aside> <h2>Categories</h2> <ul> <li><a href="#">HTML & CSS</a></li> <li><a href="#">JavaScript</a></li> <li><a href="#">Accessibility</a></li> <li><a href="#">Performance</a></li> </ul> </aside>
<footer> <p>© 2026 The Weekly Byte. All rights reserved.</p> </footer>
</body></html>Sample Run
Open `blog.html` in a browser, or use the live preview above, and try the following:
- Click "Recent Posts" or "Post Stats" in the header nav — both jump straight to that part of the page via fragment links, no JavaScript involved.
- Look at the Post Stats table — because it is marked up with real `<table>`/`<th>`/`<td>` elements, a screen reader can announce "Read Time, column 4" for any cell, something a div-based grid cannot do without extra ARIA.
- Click the video's play button — the browser's native controls (added by the `controls` attribute) handle play, pause, and volume without any extra markup.
- Resize the window and notice the sidebar categories remain a clearly separate block from the blog posts — that is the `<aside>`/`<main>` split doing its job.
Extend This Project
- Add pagination links ("Older Posts") at the bottom of the Recent Posts section using `<nav aria-label="Pagination">`.
- Turn each category in the sidebar into a link that would filter posts by that tag on a real backend.
- Add an RSS feed link in the footer using `<link rel="alternate" type="application/rss+xml">` in the `<head>`.
- Make the Post Stats table sortable by clicking column headers (a great first JavaScript project once you've finished the HTML courses).
- Add an `<audio>` element with `controls` for a podcast-episode version of the featured post.
Summary
You structured a realistic blog homepage using the sectioning elements that match what each piece of content actually is: a header and footer for page-wide content, article elements for standalone posts, a real data table for tabular statistics, an aside for tangential content, and an embedded video for multimedia. Reaching for the right landmark instead of a generic div is what lets browsers, search engines, and assistive technology all understand this page the same way a human reader does.