What is semantic HTML?
Semantic means relating to meaning. Writing semantic HTML means choosing each element for what the content is, not how it looks: appearance is the job of CSS. Choose the right element for the job and you won't need to refactor or comment your HTML later.
Div soup vs semantic markup
Two ways to mark up the same page. They look identical; only the second tells browsers, search engines and assistive technology what each part is.
<div>
<span>Three words</span>
<div>
<a>one word</a>
<a>one word</a>
</div>
</div>
<div>
<div><div>five words</div></div>
<div>
<div>three words</div>
<div>forty-six words</div>
</div>
</div>
<div><span>five words</span></div><header>
<h1>Three words</h1>
<nav>
<a>one word</a>
<a>one word</a>
</nav>
</header>
<main>
<section>
<h2>three words</h2>
<p>forty-six words</p>
</section>
</main>
<footer>
<p>five words</p>
</footer>The first gives a screen reader a flat list of text. The second gives it four landmarks to jump between straight away (banner, navigation, main and contentinfo) and gives every parser unambiguous roles.
The accessibility tree
Alongside the DOM, browsers build an accessibility tree: the version of the page that screen readers, braille displays and switch controls use. Non-semantic markup produces a flat tree of text with no roles; semantic markup produces named landmarks and a heading outline.
The role attribute: use with care
role can give any element any ARIA role, so <p role="button"> is announced as a button. But it adds no keyboard behaviour, focus or events; you'd have to build all of that yourself.
<!-- Avoid: semantics without behaviour -->
<div role="button">Click me</div>
<!-- Prefer: role, focus and keyboard support built in -->
<button type="button">Click me</button>Don't pick an element for its default look either. An <h1> isn't big bold text: it's the page's main heading. Ask what role the content plays on the page; the answer usually names the element.
Technical SEO benefits
Clear main content
Landmarks separate what is unique to a page (<main>) from the chrome repeated on every page, so parsers can tell what the page is actually about.
Content boundaries
<article> marks where a self-contained piece starts and ends, which helps search engines pick out its headline, author and date.
Markup that agrees with your schema
<time datetime> next to datePublished, or <address> next to author details: when the HTML and the JSON-LD say the same thing, there's less room for misreading.
A readable outline
A logical h1 → h2 → h3 hierarchy shows the page's topic and sub-topics at a glance, to search engines and to people skimming.
Links in context
Search engines tell boilerplate links (menus, footers) from links inside the main content, where the surrounding text gives a link its context.
A leaner DOM
Semantic elements replace layers of wrapper <div>s. A smaller DOM is cheaper to style and lay out; Lighthouse flags excessive DOM size.
Search engines can index div-only pages. Semantic HTML makes the same content easier to parse correctly: fewer chances to misread the title, the main content, the date or the author.
Benefits for AI search and agents
AI answer engines and browser agents read your HTML and accessibility tree rather than looking at the page.
Content extraction
Systems that pull text for retrieval or citation use <main>, <article> and headings to separate the primary content from sidebars, ads and footers.
Agent navigation
Browser agents often find controls through the accessibility tree: <button>, <a href>, <input>, <nav>. A <div onclick> with no role or name is easy to miss.
Answer extraction
A question answered in its own named section, with an <h2> and a <p>, is easier to lift into an answer than the same text inside a generic <div>.
Entities and facts
<address>, <time> and <article> tie facts to the right people, organisations and dates.
Tables and lists
<table> with <th scope> and <caption> keeps row and column relationships in the HTML itself. The same data in a <div> grid only makes sense visually.
Reliable action targets
Agents resolve “click Subscribe” by role and accessible name. Native <button> and <label for> resolve reliably; unlabelled custom controls don't.
Elements, roles and landmarks
| Element | Implicit role | Landmark | Use it for |
|---|---|---|---|
<header> | banner | Yes, at page level | Logo, top navigation, site search |
<nav> | navigation | Yes | Menus, breadcrumbs, pagination |
<main> | main | Yes | The page's main content (one only) |
<footer> | contentinfo | Yes, at page level | Copyright, policy links, contact |
<aside> | complementary | Yes | Sidebars, callouts, related links |
<section> | region | Only when named | A themed group with a heading |
<form> | form | Only when named | Forms with a clear purpose |
<article> | article | No | Posts, news, comments, widgets |
<figure> | figure | No | Images, charts, code with captions |
<time> | time | No | Dates and times, with datetime |
<details> / <summary> | group / button | No | Show and hide without JavaScript |
<h1>–<h6> | heading | No | The document outline |
<a href> | link | No | Navigation to a URL |
<button> | button | No | Actions on the page |
A <section> or <form> only becomes a landmark when it has an accessible name (aria-label or aria-labelledby).
Common replacements
| Instead of | Use | Why |
|---|---|---|
<div id="header"> | <header> | Banner landmark |
<div class="menu"> | <nav> | Navigation landmark |
<div id="content"> | <main> | Main landmark |
<div id="footer"> | <footer> | Contentinfo landmark |
<div class="sidebar"> | <aside> | Complementary landmark |
<div class="post"> | <article> | Self-contained content |
<span class="date"> | <time datetime> | Machine-readable date |
<p role="button"> | <button type="button"> | Keyboard support built in |