SAE Academy
Fundamentals

Semantic Elements

Why HTML has elements that describe meaning rather than appearance, and which element to use for each kind of content.

You can build a page that looks exactly like any other using nothing but <div> and <span> tags. The page will render. Text will appear. Images will load. For a sighted mouse user, nothing seems wrong.

For everyone else, the page is broken.

The problem: divitis

Divitis is the habit of using <div> for every container, <span> for every text wrapper, and nothing else. Here is a realistic blog page with divitis:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>The History of Jazz</title>
</head>
<body>
  <div class="site-header">
    <div class="logo">Jazz Times</div>
    <div class="nav">
      <div class="nav-item"><a href="/articles">Articles</a></div>
      <div class="nav-item"><a href="/about">About</a></div>
    </div>
  </div>

  <div class="main">
    <div class="article">
      <div class="article-header">
        <div class="headline">The History of Jazz</div>
        <div class="byline">By Marcus Webb</div>
      </div>
      <div class="article-body">
        <div class="paragraph">Jazz emerged in New Orleans in the early 20th century...</div>
        <div class="paragraph">The bebop movement of the 1940s...</div>
      </div>
    </div>
    <div class="sidebar">
      <div class="sidebar-heading">Related articles</div>
      <div class="sidebar-item"><a href="/miles-davis">Miles Davis: A Life</a></div>
    </div>
  </div>

  <div class="site-footer">
    <div class="copyright">2024 Jazz Times</div>
  </div>
</body>
</html>

This page looks fine visually. The problems are invisible to a sighted mouse user.

Problem 1: screen readers find no structure

Screen readers announce elements by their role: "heading level 2", "navigation landmark", "article region", "button". A <div> has no role. It is a generic container. The screen reader announces it as nothing.

A blind user navigating this page by pressing H to jump between headings will find zero headings, because <div class="headline"> has no heading role. They press R to jump between regions and find nothing. They press B for buttons and nothing. The page is a flat wall of text with no navigable structure.

The same page built with semantic elements exposes a full outline: a navigation landmark, a main content area, a heading, article sections, an aside. The user can jump directly to what they need.

Problem 2: search engines see no structure

Search engines read HTML to understand what a page is about. When they find a <h1>, they know it is the primary topic. When they find <article>, they know it is a distinct piece of content. When they find <nav>, they know to treat those links as navigation, not content.

A page of divs provides none of this. The search engine reads the text but cannot determine hierarchy or importance. Semantic structure is part of how search rankings are determined.

Problem 3: other developers cannot read your intent

<div class="article-header"> tells a reader nothing without the class. You have to read the CSS to understand what it does. <header> inside an <article> is self-explanatory.

Document landmarks

Landmarks are semantic elements that mark the major regions of a page. Assistive technology exposes them as named regions users can jump to directly.

The <header> element marks the introductory content of its parent. At the top level, it is the site header: logo, site name, and primary navigation. Inside an <article> or <section>, it is the header for that content unit.

<!-- Site-wide header -->
<header>
  <a href="/">Jazz Times</a>
  <nav>...</nav>
</header>

<!-- Article header, inside an article -->
<article>
  <header>
    <h1>The History of Jazz</h1>
    <p>By Marcus Webb, published March 2024</p>
  </header>
  <!-- article body -->
</article>

<header> is not the same as <h1>. <header> is a container for introductory content. <h1> is a heading. A <header> typically contains a heading, but a heading alone is not a header element.

The <nav> element marks a block of navigation links. Use it for the primary site navigation, for breadcrumbs, and for a table of contents.

<nav aria-label="Primary navigation">
  <ul>
    <li><a href="/articles">Articles</a></li>
    <li><a href="/archive">Archive</a></li>
    <li><a href="/about">About</a></li>
  </ul>
</nav>

When you have more than one <nav> on a page, add an aria-label to distinguish them. Without a label, a screen reader user hears "navigation" twice and cannot tell which is which. With labels, they hear "Primary navigation" and "Footer navigation".

<nav aria-label="Primary navigation">...</nav>

<!-- ... page content ... -->

<footer>
  <nav aria-label="Footer navigation">...</nav>
</footer>

Not every group of links needs a <nav>. A list of links in an article body, or links in a widget, do not need <nav>. Reserve it for major navigation blocks.

<main>

The <main> element wraps the primary content of the page — everything that is not navigation, header, or footer. Every page should have exactly one <main>.

<body>
  <header>...</header>
  <nav>...</nav>

  <main>
    <article>
      <h1>The History of Jazz</h1>
      <!-- article content -->
    </article>
  </main>

  <footer>...</footer>
</body>

Screen reader users can press a keyboard shortcut to jump directly to <main>, skipping the header and navigation. This is why skip links (covered in the Accessibility lesson) target <main>: users who navigate with a keyboard do not want to tab through the entire navigation on every page load.

Never place two <main> elements on the same page. If the second <main> is hidden, it is fine to have one visible and one with hidden attribute. But two visible <main> elements are invalid.

The <footer> element marks the footer of its parent. The site-wide footer contains copyright information, secondary links, and legal text. An <article> can also have a footer for metadata like tags, publication date, or author bio.

<!-- Site-wide footer -->
<footer>
  <nav aria-label="Footer navigation">
    <a href="/privacy">Privacy</a>
    <a href="/terms">Terms</a>
  </nav>
  <p>2024 Jazz Times. All rights reserved.</p>
</footer>

<!-- Article footer -->
<article>
  <!-- article content -->
  <footer>
    <p>Filed under: <a href="/tag/jazz">Jazz</a>, <a href="/tag/history">History</a></p>
  </footer>
</article>

<aside>

The <aside> element marks content that is tangentially related to the content around it. The key word is tangentially: an aside is related to the main content but not essential to understanding it. You could remove it and the main content would still make sense.

Common uses: a pull quote, a list of related articles, an author bio in a sidebar, an advertisement.

<main>
  <article>
    <h1>The History of Jazz</h1>
    <p>Jazz emerged in New Orleans...</p>
  </article>

  <aside aria-label="Related articles">
    <h2>Related reading</h2>
    <ul>
      <li><a href="/bebop">The Bebop Revolution</a></li>
      <li><a href="/coltrane">John Coltrane's Legacy</a></li>
    </ul>
  </aside>
</main>

Screen readers announce <aside> as a "complementary" landmark. If you have more than one <aside> on a page, use aria-label on each to distinguish them.

Do not use <aside> for content that is integral to understanding the main content. If removing it would make the page confusing or incomplete, it should be inside the main content flow, not in an aside.

Content grouping

<article>

An article is a self-contained piece of content. Self-contained means it makes sense on its own, outside the context of the page. You could copy an article to a different website, an RSS feed, or a newsletter, and it would still make sense.

Use <article> for:

  • Blog posts
  • News articles
  • Product cards in a listing
  • Comments on a post (each comment is self-contained)
  • Social media posts embedded in a page
<!-- A blog post -->
<article>
  <header>
    <h2><a href="/article/history-of-jazz">The History of Jazz</a></h2>
    <time datetime="2024-03-10">March 10, 2024</time>
  </header>
  <p>Jazz emerged in New Orleans in the early 20th century...</p>
  <footer>
    <p>By Marcus Webb</p>
  </footer>
</article>

Articles can nest. A blog post is an <article> that contains <article> elements for each comment, because comments are self-contained responses.

<section>

A section is a thematic grouping of content. It belongs to the page it is on and does not make sense in isolation. A section almost always has a heading.

Use <section> for:

  • Chapters within a long article
  • Grouped content on a landing page (features section, pricing section, team section)
  • Tabs in a tabbed interface
<!-- Sections within a long article -->
<article>
  <h1>A Guide to Audio Equipment</h1>

  <section>
    <h2>Headphones</h2>
    <p>...</p>
  </section>

  <section>
    <h2>Speakers</h2>
    <p>...</p>
  </section>

  <section>
    <h2>Amplifiers</h2>
    <p>...</p>
  </section>
</article>

If a <section> has no heading, it is a signal that you might be using the wrong element. A section without a heading is often a <div>.

<figure> and <figcaption>

A <figure> is an image, diagram, code block, or other media that is referenced from the main content but could be moved to an appendix without disrupting the flow. <figcaption> provides the caption.

<figure>
  <img src="miles-davis-1959.jpg" alt="Miles Davis playing trumpet on stage in 1959" />
  <figcaption>Miles Davis performing at the Newport Jazz Festival, 1959.</figcaption>
</figure>

The <figure> groups the image and its caption as a unit. The browser understands they belong together. Screen readers announce the caption as describing the image. This is covered in more detail in the Images lesson.

The decision: div vs section vs article

This decision trips up developers at every level. Here is how to make it.

Ask three questions in order:

1. Does the content make sense independently, extracted and shown elsewhere?

A blog post makes sense on its own. A product card makes sense on its own. A user comment makes sense on its own. Use <article>.

2. Is this content a thematic group that belongs specifically to this page, with a heading that titles the group?

A "Features" section on a landing page. A "Reviews" section below a product. Chapters in a guide. Use <section>.

3. Is this purely a layout container, with no inherent meaning?

A wrapper div to centre content. A flex container for a two-column layout. A group of elements you need to target with a single CSS selector. Use <div>.

The test for <article> is syndication: could this appear in an RSS reader, a social preview, or a different site and still make sense? If yes, it is an <article>.

The test for <section> is the heading: does this group have a heading that titles its content? If not, it is probably a <div>.

<!-- Article: self-contained, could be syndicated -->
<article>
  <h2>Product: Wireless Headphones</h2>
  <p>Price: $79.99</p>
  <p>In-ear, 30-hour battery, active noise cancellation.</p>
</article>

<!-- Section: thematic group with a heading, belongs to the page -->
<section>
  <h2>Customer reviews</h2>
  <!-- reviews here -->
</section>

<!-- Div: layout container, no semantic meaning -->
<div class="two-column-layout">
  <main>...</main>
  <aside>...</aside>
</div>

Interactive native elements

Two elements replace common patterns that are often built with divs and JavaScript. They are worth knowing now, though they are covered in depth in the Interactive Elements lesson.

<details> and <summary> create a disclosure widget: a collapsed section that expands when clicked. This replaces the "accordion" pattern that used to require custom JavaScript.

<details>
  <summary>What is your return policy?</summary>
  <p>You can return any item within 30 days of purchase for a full refund.</p>
</details>

<dialog> creates a modal or non-modal dialog. It manages focus trapping, Escape key closing, and ARIA roles natively. This replaces <div class="modal"> plus a large amount of custom JavaScript.

Common mistakes

Using <section> as a styled box. A section implies thematic meaning and should have a heading. If you are using <section> to create a visually boxed area with no semantic grouping, use <div>.

<!-- Wrong: section used as a styled container with no heading -->
<section class="hero-image-wrapper">
  <img src="hero.jpg" alt="..." />
</section>

<!-- Correct: div for layout containers -->
<div class="hero-image-wrapper">
  <img src="hero.jpg" alt="..." />
</div>

Multiple <main> elements. There is one primary content area per page. Two <main> elements are invalid.

Nesting <main> inside <article>. The <main> element represents the primary content of the page, not the primary content of an article. It belongs as a direct child of <body>.

Using <aside> for content that is integral. If removing the aside would make the page confusing, it should be in the main flow. An aside is supplementary, not essential.

Missing aria-label when landmarks repeat. Two <nav> elements, two <aside> elements, or two <footer> elements on a page need aria-label to distinguish them. Without it, a screen reader user hears the same landmark name twice and cannot tell which is which.

<!-- Wrong: two nav elements with no labels -->
<nav>...</nav>
<footer>
  <nav>...</nav>
</footer>

<!-- Correct: aria-label distinguishes them -->
<nav aria-label="Primary navigation">...</nav>
<footer>
  <nav aria-label="Footer navigation">...</nav>
</footer>

Exercise

Build the structure of a blog post page using only semantic elements. No <div> allowed. The page should have:

  • A site header with a site name and a primary navigation link list
  • A main content area containing a single article
  • Inside the article: a header (with <h1> and a byline), a section for the article body with at least two paragraphs, and a footer with tag links
  • An aside with a "Related posts" heading and a short list of links
  • A site footer with a copyright line

Focus on structure only. The CSS will provide minimal styling so you can see the landmarks.

On this page