Skip to main content

🏷️ Semantic HTML Elements

You can build an entire website out of nothing but <div> tags — and it will look fine. But it will be invisible to screen readers, harder for search engines to understand, and a nightmare for the next developer to read. Semantic HTML fixes all three by giving your content meaning, not just shape.

Week 1 · Day 2 (Tuesday: HTML Fundamentals) · Lecture 2

🎯 Learning Objectives

By the end of this lesson, you will be able to:

  • Explain what "semantic" means and why it beats <div> soup
  • Lay out a page with the structural landmarks: header, nav, main, article, section, aside, and footer
  • Choose correctly between <article> and <section>
  • Build a valid document outline with the h1h6 heading hierarchy
  • Apply inline semantics like <strong>, <em>, <time>, and <abbr>
  • Mark up images, quotes, and figures accessibly

Estimated Time: 60 minutes

Practice: Convert a page of anonymous divs into a meaningful, semantic blog layout.

In This Lesson

What Is Semantic HTML?

Imagine reading a book printed as one unbroken block of text — no chapters, no headings, no paragraphs. You could still read every word, but finding anything would be exhausting. Semantic HTML is the difference: it uses tag names that describe what the content is, so both humans and machines can navigate it.

Compare these two versions of the same navigation bar:

<!-- Non-semantic: works visually, means nothing -->
<div class="top">
    <div class="menu">
        <div class="link">Home</div>
    </div>
</div>

<!-- Semantic: the tags tell the story -->
<header>
    <nav>
        <a href="/">Home</a>
    </nav>
</header>

Both render identically. But only the second tells a screen reader "this is the site header, and this is the navigation," lets a keyboard user jump straight to it, and signals to search engines which links form your main menu.

graph LR A[Non-semantic: div soup] --> A1[No meaning] A --> A2[Hard to navigate] A --> A3[Poor accessibility] B[Semantic HTML] --> B1[Clear meaning] B --> B2[Screen-reader landmarks] B --> B3[Better SEO]

Why it matters

  • Accessibility: Screen readers expose semantic elements as "landmarks" users can jump between
  • SEO: Search engines weight content inside meaningful tags more sensibly
  • Maintainability: <article> reads better than <div class="art-wrap-2">
  • Future-proofing: New tools and browsers understand standard elements automatically

Structural Landmarks

HTML5 gives you a set of container elements that mark the major regions of a page — the same way a newspaper has a masthead, sections, sidebars, and a footer. Screen readers call these landmarks, and users navigate between them directly.

A typical page layout showing header, nav, main with article and aside, and footer <header> <nav> <main> <article> <aside> <footer>
The structural landmarks divide a page into regions that assistive technology and search engines both understand.
ElementWhat it marks
<header>Introductory content — logo, site title, sometimes the nav
<nav>A block of major navigation links
<main>The page's primary, unique content — exactly one per page
<article>A self-contained piece that could stand alone (a post, a card)
<section>A thematic grouping of related content, usually with a heading
<aside>Tangential content — sidebars, pull quotes, related links
<footer>Closing content — copyright, contact, secondary links

Here's a skeleton page using all of them together:

<body>
    <header>
        <h1>My Blog</h1>
        <nav>
            <a href="/">Home</a>
            <a href="/about">About</a>
        </nav>
    </header>

    <main>
        <article>
            <h2>A Post Title</h2>
            <p>The content of the post...</p>
        </article>
    </main>

    <aside>
        <h2>Related</h2>
        <a href="/other">Another post</a>
    </aside>

    <footer>
        <p>&copy; 2026 My Blog</p>
    </footer>
</body>

⚠️ One <main>, and don't over-<nav>

There should be exactly one <main> per page — it's the "skip to content" target. And <nav> is for major navigation blocks only; a stray link inside a paragraph does not need to be wrapped in <nav>.

Article vs. Section

This is the pairing that trips up almost everyone. Both group content — so which do you reach for?

  • <article> — use it when the content would make sense on its own, lifted out of the page. A blog post, a news story, a product card, a single comment. The "could I syndicate this in an RSS feed?" test is a good one.
  • <section> — use it to group related content within a larger whole, almost always introduced by a heading. "Our Services," "Chapter 2," "Frequently Asked Questions."
<article>
    <h2>How to Brew Better Coffee</h2>

    <section>
        <h3>Choosing Beans</h3>
        <p>...</p>
    </section>

    <section>
        <h3>Grinding</h3>
        <p>...</p>
    </section>
</article>
💡 Rule of thumb: An article is a whole thing you could hand to someone by itself. A section is a labelled chunk inside a bigger thing. It's perfectly normal for articles to contain sections — and occasionally the reverse.

💡 When neither fits, use a <div>

Semantic elements describe meaning. If you only need a box to hook some CSS onto — a wrapper for layout with no meaning of its own — a plain <div> is the correct choice. Don't force semantics where there are none.

Headings & Outline

Headings <h1> through <h6> aren't just "big bold text" — they create a document outline, like a table of contents built into your markup. Screen-reader users routinely pull up a list of all headings to jump around a page, so getting the hierarchy right is a genuine accessibility feature, not a cosmetic one.

<h1>Page Title (one per page)</h1>
    <h2>Major Section</h2>
        <h3>Subsection</h3>
        <h3>Another Subsection</h3>
    <h2>Another Major Section</h2>
graph TD H1[h1: Page Title] --> H2A[h2: Section 1] H1 --> H2B[h2: Section 2] H2A --> H3A[h3: Subsection 1.1] H2A --> H3B[h3: Subsection 1.2] H2B --> H3C[h3: Subsection 2.1]

⚠️ Don't skip levels, and don't pick by size

Go h1 → h2 → h3 in order; never jump from h1 straight to h4 because it "looks right." If a heading is too big or too small, that's a job for CSS, not for choosing a different heading level. The level communicates structure; the styling is separate.

Text Content Elements

Beyond headings, a handful of block-level elements organize the bulk of your text.

Paragraphs and lists

<p>A paragraph is the default container for a block of text.</p>

<!-- Unordered list: order doesn't matter -->
<ul>
    <li>Coffee</li>
    <li>Tea</li>
</ul>

<!-- Ordered list: sequence matters -->
<ol>
    <li>Boil the water</li>
    <li>Add the grounds</li>
</ol>

<!-- Description list: term/definition pairs -->
<dl>
    <dt>HTML</dt>
    <dd>HyperText Markup Language</dd>
</dl>

Quotes

Use <blockquote> for a standalone quoted passage and <cite> to name its source. The cite attribute can point to the original URL.

<blockquote cite="https://www.w3.org/People/Berners-Lee/">
    <p>The Web is for everyone.</p>
    <footer>— <cite>Tim Berners-Lee</cite></footer>
</blockquote>

Inline Semantics

Inline elements add meaning to small stretches of text within a paragraph. The key insight: choose them for meaning, not appearance. <strong> and <b> both look bold, but only <strong> tells assistive tech "this is important."

Emphasis and importance

<p>This word is <em>emphasized</em> (usually italic).</p>
<p>This word is <strong>important</strong> (usually bold).</p>

Useful inline elements

<!-- Abbreviation with an expansion on hover -->
<abbr title="HyperText Markup Language">HTML</abbr>

<!-- Machine-readable date/time -->
<time datetime="2026-01-15">January 15, 2026</time>

<!-- Highlighted / marked text -->
Results for <mark>semantic HTML</mark>

<!-- Inline code -->
Call the <code>console.log()</code> function.

<!-- Chemistry and math -->
H<sub>2</sub>O and E = mc<sup>2</sup>

Links

The anchor <a> is the "hyper" in HyperText. Its href can point to many kinds of destination:

<a href="https://example.com" target="_blank" rel="noopener">External site</a>
<a href="#section1">Jump to a section on this page</a>
<a href="mailto:hello@example.com">Email us</a>
<a href="tel:+15551234567">Call us</a>
💡 Security note: When you open a link in a new tab with target="_blank", add rel="noopener". It prevents the new page from gaining scripting access back to yours.

Media & Figures

Images with meaningful alt text

Every content image needs an alt attribute describing it — this is what a screen reader announces and what displays if the image fails to load.

<img src="team.jpg" alt="Four developers collaborating around a laptop"
     width="800" height="600">
💡 Purely decorative image? Give it an empty alt="". That tells assistive tech to skip it, rather than announcing a distracting filename.

Figure and figcaption

When an image, chart, or code sample needs a caption, wrap the pair in a <figure>. It ties the caption to the content programmatically.

<figure>
    <img src="chart.png" alt="Bar chart of quarterly sales, up 25%">
    <figcaption>Quarterly sales rose 25% over last year.</figcaption>
</figure>

Audio and video

HTML5 plays media natively. Offer multiple <source> formats so different browsers each find one they support, and include a caption <track> for accessibility.

<video controls width="640" poster="thumbnail.jpg">
    <source src="clip.mp4" type="video/mp4">
    <source src="clip.webm" type="video/webm">
    <track kind="captions" src="captions_en.vtt" srclang="en" label="English">
    Your browser doesn't support embedded video.
</video>

Practice & Quiz

🏋️ Exercise: De-soup a blog post

Goal: The markup below works visually but is meaningless <div> soup. Rewrite it using semantic elements — landmark containers, a proper heading, and a machine-readable date.

<div class="top">
    <div class="title">My Blog</div>
    <div class="menu"><div class="item">Home</div></div>
</div>
<div class="content">
    <div class="post">
        <div class="post-title">My First Post</div>
        <div class="date">January 15, 2026</div>
        <div class="body">Hello, world!</div>
    </div>
</div>
💡 Hint

The outer top is a <header> containing a heading and a <nav>. The content is <main>. Each post is an <article>. Wrap the date in a <time datetime="...">.

✅ Solution
<header>
    <h1>My Blog</h1>
    <nav>
        <a href="/">Home</a>
    </nav>
</header>

<main>
    <article>
        <h2>My First Post</h2>
        <p>Published on
            <time datetime="2026-01-15">January 15, 2026</time>
        </p>
        <p>Hello, world!</p>
    </article>
</main>

Same appearance, but now every region is a landmark and the date is machine-readable.

🎯 Quick Quiz

Question 1: How many <main> elements should a single page have?

Question 2: You're marking up a blog post that could be republished on its own in an RSS feed. Which element fits best?

Question 3: Which is the correct way to mark decorative image so screen readers skip it?

Best Practices & Pitfalls

✅ Do

  • Reach for a semantic element before a <div> — use the div only when no meaning applies
  • Keep exactly one <main> and one logical <h1> per page
  • Follow the heading hierarchy in order, without skipping levels
  • Write descriptive alt text for meaningful images, alt="" for decorative ones
  • Choose <strong>/<em> for meaning, and leave visual weight to CSS

❌ Don't

  • Build the whole page from <div>s ("div soup")
  • Pick heading levels by how big they look
  • Wrap every single link in a <nav>
  • Use a <section> with no heading just to group things — reach for <div> there

✅ The one-question test

Before typing <div>, ask: "Is there a tag that describes what this is?" If yes — header, nav, article, figure, time — use it. If genuinely not, <div> is the right, honest choice.

Summary

🎉 Key Takeaways

  • Semantic HTML describes what content means, improving accessibility, SEO, and readability
  • The landmarks — header, nav, main, article, section, aside, footer — carve a page into navigable regions
  • Article = self-contained; section = a labelled group within a whole; div = meaningless wrapper
  • Headings build an outline: one h1, no skipped levels, size handled by CSS
  • Inline tags like strong, em, time, abbr add meaning to small spans of text

📚 Additional Resources

🚀 What's Next?

You can now structure and describe content meaningfully. Next we make that content interactive: the following lesson covers forms and input types — how to collect information from users with text fields, checkboxes, dropdowns, and HTML5's built-in validation.

🏷️ Meaning, not just markup!

Your pages now speak to browsers, search engines, and assistive tech alike. That's the mark of a professional front-end.