HTML Semantic Elements
HTML semantic elements are tags that describe the meaning and role of the content they wrap, not just how it should look. Instead of building every page out of generic <div> and <span> containers, you can use elements like <header>, <nav>, <main>, <article>, <section>, <aside>, and <footer> to tell the browser, search engines, and assistive technologies what each part of the page actually represents. This makes markup easier to read, easier to maintain, and dramatically more accessible to people using screen readers or other assistive tools.
Overview / How It Works
The HTML specification splits elements into categories, and one of the most important is sectioning content and related structural elements collectively known as “semantic” elements. A semantic element’s name conveys its purpose. Compare <div class="nav"> with <nav> — both can hold the same links, but only the second one tells every consumer of the page, including a browser’s built-in accessibility tools, that this block is navigation.
The most commonly used semantic sectioning elements are:
<header>— introductory content or navigational aids for its nearest sectioning ancestor (or the whole page if at the top level). A page can have one page-level header and additional headers inside articles or sections.<nav>— a block of major navigation links (primary menu, table of contents, pagination). Not every group of links needs<nav>— reserve it for significant navigation blocks.<main>— the dominant, unique content of the document. There must be only one visible<main>per page, and it must not be nested inside<article>,<aside>,<header>,<footer>, or<nav>.<article>— a self-contained piece of content that would make sense on its own if syndicated elsewhere: a blog post, a forum comment, a news story, a product card.<section>— a thematic grouping of content, generally introduced by its own heading. Use it to break a long document into labeled parts.<aside>— content that is tangentially related to the surrounding content, such as a sidebar, pull quote, or list of related links.<footer>— footer information for its nearest sectioning ancestor: author info, copyright, related links.
Alongside these, HTML offers smaller semantic elements for specific jobs: <figure> and <figcaption> group an illustration, diagram, or code sample with its caption; <time> marks up a machine-readable date or time; <mark> highlights text that is relevant in the current context; and <details>/<summary> create a native expand-and-collapse disclosure widget with no scripting required.
By default, most of these elements render exactly like a <div>: the browser’s built-in style sheet gives them display: block and no other visual styling, so switching from <div> to <section> changes nothing visually. What it does change is the accessibility tree the browser builds alongside the DOM. Many semantic elements map to implicit ARIA landmark roles — <header> becomes role banner (at the page level), <nav> becomes navigation, <main> becomes main, <aside> becomes complementary, <footer> becomes contentinfo (at the page level), and <article> becomes article. Screen reader users can pull up a list of these landmarks and jump straight to the part of the page they need, the same way a sighted user visually scans a page for the navigation bar or footer.
Syntax
<header>...</header>
<nav>...</nav>
<main>...</main>
<article>...</article>
<section>...</section>
<aside>...</aside>
<footer>...</footer>
Each of these is a standard container element: an opening tag, content, and a matching closing tag. None require special attributes to function, though id is common (for linking or styling) and class is common (for styling). The table below summarizes the core elements and the implicit role each contributes to the accessibility tree.
| Element | Purpose | Implicit ARIA role |
|---|---|---|
<header> |
Intro content / nav aids for its section | banner (page-level only) |
<nav> |
Major navigation links | navigation |
<main> |
Primary, unique page content | main |
<article> |
Self-contained, reusable content | article |
<section> |
Thematic grouping with a heading | region (when labeled) |
<aside> |
Tangential content, sidebars | complementary |
<footer> |
Footer info for its section | contentinfo (page-level only) |
Examples
Example 1: A full semantic page skeleton
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Local Coffee Guide</title>
</head>
<body>
<header>
<h1>Local Coffee Guide</h1>
<nav>
<ul>
<li><a href="#roasters">Roasters</a></li>
<li><a href="#brewing">Brewing Tips</a></li>
</ul>
</nav>
</header>
<main>
<section id="roasters">
<h2>Featured Roasters</h2>
<p>A rotating list of local coffee roasters worth visiting.</p>
</section>
<section id="brewing">
<h2>Brewing Tips</h2>
<p>Simple pour-over and French press techniques for beginners.</p>
</section>
</main>
<aside>
<h2>Did You Know?</h2>
<p>Coffee was first cultivated in Ethiopia over a thousand years ago.</p>
</aside>
<footer>
<p>© 2026 Local Coffee Guide. All rights reserved.</p>
</footer>
</body>
</html>
Result: Visually, the page looks like plain stacked blocks: a title and menu at the top, two labeled content blocks in the middle, a “Did You Know?” box, and a copyright line at the bottom — nothing more styled than a page built from <div>s. The difference is invisible to the eye but significant to a screen reader or search engine crawler, which now sees a page with a clearly labeled banner, navigation menu, main content region, two content regions, a complementary aside, and a footer.
Notice that <main> appears exactly once, and both <section> elements have their own heading (<h2>), which is what makes them meaningful thematic groupings rather than arbitrary wrappers.
Example 2: An article with a figure and a published date
<article>
<header>
<h1>Brewing the Perfect Pour-Over</h1>
<p>Published on <time datetime="2026-07-20">July 20, 2026</time> by Priya Nair</p>
</header>
<p>A pour-over is one of the simplest ways to make a clean, flavorful cup of coffee at home.</p>
<figure>
<img src="pour-over.jpg" alt="A glass pour-over dripper sitting on top of a mug">
<figcaption>A ceramic pour-over dripper with a paper filter.</figcaption>
</figure>
<p>Start with freshly ground beans and water just off the boil for the best extraction.</p>
<footer>
<p>Filed under: <a href="/brewing">Brewing Guides</a></p>
</footer>
</article>
Result: The browser renders a heading, a byline sentence with the date shown as plain text (“July 20, 2026”), a paragraph, an image with its caption centered underneath it, another paragraph, and a small footer line linking to “Brewing Guides” — all stacked vertically with default spacing. Even though the date is displayed as readable text, the datetime="2026-07-20" attribute gives machines (search engines, calendar-import tools, browser extensions) an unambiguous, parseable value to work with. This <header> and <footer> are scoped to the <article>, not the whole page, which is perfectly valid — a page can contain many local headers and footers as long as none of them is nested inside another header or footer.
Example 3: Highlighted text and a disclosure widget
<article>
<h2>Espresso Machine Troubleshooting</h2>
<p>If your shots are pulling too fast, the most common cause is a <mark>grind that's too coarse</mark>.</p>
<details>
<summary>My espresso tastes sour — what's wrong?</summary>
<p>A sour taste usually means under-extraction. Try grinding finer, increasing the dose, or extending the shot time.</p>
</details>
<details>
<summary>My espresso tastes bitter — what's wrong?</summary>
<p>Bitterness usually means over-extraction. Try grinding coarser or shortening the shot time.</p>
</details>
</article>
Result: The phrase “grind that’s too coarse” appears with a yellow highlight background by default, since <mark> is one of the few semantic elements with built-in visual styling. Below it, two collapsed question rows appear, each with a small triangle/arrow marker; clicking a question (the <summary>) expands it in place to reveal its answer paragraph, and clicking again collapses it — all without a single line of JavaScript.
How It Works Step by Step
When the browser receives HTML, its parser tokenizes the markup tag by tag and builds the DOM tree exactly as it would for <div> elements — a semantic element is not “special” syntax, it is simply an element name the parser already recognizes from the HTML spec’s list of known tags. Once the DOM tree exists, two things happen in parallel:
- The rendering engine applies the browser’s default (user-agent) style sheet, which gives sectioning elements
display: blockand gives a handful of elements (like<mark>, and the marker triangle on<summary>) a small amount of default visual styling. Everything else about layout and appearance is left to your own CSS. - The browser walks the DOM again to build the accessibility tree, a parallel structure used by screen readers and other assistive technology. During this pass, it looks up each element’s implicit ARIA role from the HTML spec’s mapping table — this is how
<nav>becomes a “navigation” landmark and<main>becomes the “main” landmark without you writing a singleroleattribute.
Because landmark roles are derived from the DOM, misusing an element (for example, adding a second <main>, or wrapping unrelated content in one <article>) doesn’t throw an error; the page still renders. But it corrupts the landmark map that assistive technology relies on, which is why semantic correctness is treated as seriously as syntactic correctness in HTML.
Common Mistakes
Mistake 1: “Div soup” instead of semantic landmarks
Wrapping every section of a page in generically named <div>s works visually but throws away all the structural meaning a browser or assistive technology could use:
<div class="header">
<div class="title">My Blog</div>
<div class="nav">
<div><a href="/">Home</a></div>
<div><a href="/about">About</a></div>
</div>
</div>
<div class="main">
<div class="post">
<div class="post-title">Hello World</div>
<div class="post-body">This is my first post.</div>
</div>
</div>
<div class="footer">Copyright 2026</div>
This is not invalid markup — it will render fine — but it is invisible to a screen reader’s landmark list, gives search engines no structural signal, and the “title” isn’t even a real heading element. The fix is to swap the meaningless wrappers for the semantic elements that already exist for this exact purpose:
<header>
<h1>My Blog</h1>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
</header>
<main>
<article>
<h2>Hello World</h2>
<p>This is my first post.</p>
</article>
</main>
<footer>
<p>Copyright 2026</p>
</footer>
Mistake 2: Using more than one page-level <main>
Because <main> is meant to identify the single dominant content region of the page, adding a second one breaks that contract:
<main>
<h1>Product Catalog</h1>
<p>Browse our items below.</p>
</main>
<main>
<h2>Customer Reviews</h2>
<p>See what shoppers are saying.</p>
</main>
A screen reader’s “jump to main content” shortcut becomes ambiguous with two candidates, and the spec explicitly disallows more than one visible <main> per page. Nest the second block inside a labeled <section> within the same <main> instead:
<main>
<h1>Product Catalog</h1>
<p>Browse our items below.</p>
<section>
<h2>Customer Reviews</h2>
<p>See what shoppers are saying.</p>
</section>
</main>
Best Practices
- Use exactly one
<main>per page, containing only content unique to that page (skip repeated site chrome like the nav or footer). - Give every
<section>a heading; if a block of content has no natural heading, it’s probably better as a plain<div>. - Reserve
<article>for content that would still make sense if pulled out and displayed somewhere else on its own (a blog post, a comment, a product listing). - Nest local
<header>/<footer>elements inside<article>or<section>when the meta-information (byline, tags, date) belongs to that block specifically, not the whole page. - Don’t reach for
<nav>for every group of links — use it for primary/major navigation, not for a single inline link or a small list of tags. - Pair every
<figure>with a<figcaption>so images, diagrams, or code samples keep their caption programmatically associated. - Remember that semantic elements carry no visual styling of their own (besides
<mark>and the<details>marker) — layout and appearance are the job of CSS, covered in the CSS course. - Test your structure by opening your browser’s accessibility inspector and checking that the landmarks (banner, navigation, main, complementary, contentinfo) match what you intended.
Practice Exercises
- Take a page you’ve built (or imagine one) with a header, a two-column layout, and a footer, and mark it up using
<header>,<main>,<aside>, and<footer>. Make sure only one<main>appears. - Write a recipe article using
<article>, with a<header>containing the recipe title and a<time>element for when it was published, plus a<figure>/<figcaption>for a photo of the finished dish. - Build a small FAQ section using three
<details>/<summary>pairs, each with a question as the summary and a one-sentence answer as the body.
Summary
- Semantic elements describe what content is, not how it looks — presentation is CSS’s job.
<header>,<nav>,<main>,<article>,<section>,<aside>, and<footer>are the core structural building blocks of a page.- Most semantic elements map to implicit ARIA landmark roles, which is how screen readers let users jump directly to navigation, main content, or the footer.
- There must be only one visible
<main>per page, and headers/footers can be scoped locally to an article or section. <figure>/<figcaption>,<time>,<mark>, and<details>/<summary>handle smaller but equally meaningful jobs.- Semantic markup renders the same as a well-styled
<div>-based page, but is far more useful to search engines, assistive technology, and future maintainers of your code.
