LearnAI ToolsCareerPractice BuildsPlayContact
HTMLBeginner~1.5 hours

Blog Homepage Layout

Structure a multi-article blog homepage using semantic sectioning elements.

Semantic HTMLTablesMultimedia

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.

What You'll Build
  • 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 &mdash; <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 &mdash; 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 &mdash; <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 &rarr;</a>
</article>
<article>
<h3>A Beginner's Guide to ARIA Landmarks</h3>
<p>By Asha Verma &mdash; <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 &rarr;</a>
</article>
<article>
<h3>Speeding Up Page Loads With Lazy-Loaded Images</h3>
<p>By Karan Singh &mdash; <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 &rarr;</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 &amp; 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>&copy; 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 &mdash; <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 &mdash; 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 &mdash; <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 &rarr;</a>
</article>
<article>
<h3>A Beginner's Guide to ARIA Landmarks</h3>
<p>By Asha Verma &mdash; <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 &rarr;</a>
</article>
<article>
<h3>Speeding Up Page Loads With Lazy-Loaded Images</h3>
<p>By Karan Singh &mdash; <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 &rarr;</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 &amp; 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>&copy; 2026 The Weekly Byte. All rights reserved.</p>
</footer>
</body>
</html>
Live Preview

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.