Semantic sections
header, nav, main, article, section, aside, footer — and friends. When to use which, and when div is still the right call.
Before HTML5, every region of a page was a <div> with a class — <div class="header">, <div class="sidebar">, <div class="article">. The browser had no idea which div was the navigation and which was the footer; assistive technology had to guess from class names. HTML5 added a small vocabulary of semantic elements that name those regions directly.
This isn't aesthetic preference. Screen readers expose a "landmarks" navigation mode where users jump straight to main, nav, or footer. Search engines use the structure to identify the primary content. Reader-mode browsers strip everything that isn't <article> or <main>. None of that fires on <div class="header">.
Page-level landmarks
Four elements carry the load:
<header>— introductory content for the page or for a section. Most often the site's banner: logo, top-level nav, search. Inside an<article>, it's the article's own header (title, byline, date).<nav>— a major block of navigation links. Reserved for the main navigation regions, not every group of links. The site's primary nav, a table of contents, paginated links between articles — yes. Three social-media icons in the footer — no.<main>— the primary content of the document, exclusive of headers, footers, and sidebars. There must be exactly one<main>per page.<footer>— closing content for the page or section. Author info, copyright, related links.
<body>
<header>
<a href="/"><img src="logo.svg" alt="Acme"></a>
<nav aria-label="Primary">
<a href="/products">Products</a>
<a href="/pricing">Pricing</a>
<a href="/docs">Docs</a>
</nav>
</header>
<main>
<article>
<h1>Article title</h1>
<p>...</p>
</article>
</main>
<footer>
<p>© 2025 Acme</p>
</footer>
</body><header> and <footer> aren't unique. A page can have one outer header, plus an inner header for each <article> — they nest, and the meaning ("introductory content for this sectioning element") still holds.
<header> belong?Article vs section
The two get confused because both contain headings and nested content. They mean different things.
<article> is self-contained. If you copy it into a different context — an aggregator, an email digest, a social-media unfurl — it still makes sense on its own. Blog posts, news stories, forum threads, product cards on a listing page. The test: would this make sense as the only thing on a page?
<section> is a chapter — a thematic grouping of content that belongs to a larger whole. It needs a heading. A long article might be split into several sections, each covering a sub-topic.
The lazy default <div> doesn't claim either. Reach for <article> when the chunk could stand alone, <section> when it's a labeled subdivision of something larger, and <div> when it's pure styling scaffolding with no semantic claim to make.
<article> <h1>The HTML parser is more interesting than you'd think</h1> <section> <h2>Tokenization</h2> <p>...</p> </section> <section> <h2>Tree construction</h2> <p>...</p> </section> </article>
Don't use <section> as a fancy <div>. A section without a heading is a smell — you're claiming "thematic grouping" but not labeling the theme. If there's no heading and no clear topic, it's a div.
Aside, time, address
Three smaller elements with specific jobs:
<aside>— content tangentially related to the main flow. A sidebar, a pull quote, a "related articles" block, an ad. Not "anything visually off to the side" — it has to be tangential. A piece of the article's main argument that happens to be in a sidebar is still part of the article.<time>— a date, time, or duration. Thedatetimeattribute carries a machine-readable ISO 8601 value:<time datetime="2025-04-26">April 26th</time>. Crawlers and calendar tools can parse the value without trying to parse the human prose.<address>— contact information for the article's or page's author. Despite the name, it's not for any address — a venue's street address inside a story is just a<p>. It's specifically who to contact about this content, usually inside an article footer.
<article>
<header>
<h1>HTML basics</h1>
<p>By <address>Ada Lovelace</address>, published
<time datetime="2025-04-26">26 April 2025</time>.</p>
</header>
...
</article>details, summary, dialog
Two interactive elements the platform gives you for free.
<details> is a disclosure widget — a collapsed region that opens when you click its summary. <summary> is the always-visible label. The open boolean attribute starts it expanded. No JavaScript required: the open/close is built into the browser.
<details> <summary>Why is the parser so forgiving?</summary> <p>Because the early web's HTML was so sloppy that strict parsers would have rendered most of it as error pages...</p> </details>
<dialog> is a modal or non-modal popup. Open it from JavaScript with dialog.showModal() (modal — backdrop, traps focus) or dialog.show() (non-modal). Close it with dialog.close() or by submitting a form with method="dialog". The browser handles focus trapping, the ::backdrop pseudo-element, and keyboard escape — all the things you'd otherwise build by hand for a modal.
<dialog id="confirm">
<form method="dialog">
<p>Delete this draft?</p>
<button value="cancel">Cancel</button>
<button value="delete">Delete</button>
</form>
</dialog>
<button onclick="document.getElementById('confirm').showModal()">
Delete draft
</button>When div is the right answer
A <div> is a container with no semantic meaning. That isn't a failure; sometimes "no semantic meaning" is the truth. A wrapper element that exists only to apply a CSS grid, or to group two paragraphs for a Tailwind class, has nothing to claim about its content.
Reaching for <section> or <article> when the chunk doesn't actually fit — because "div feels lazy" — pollutes the accessibility tree the same way using a table for layout does. Screen readers will announce a section landmark that doesn't exist, and users navigating by landmarks will end up in a region that has no thematic identity.
The shape of the rule: start with <div> and earn your way up. If the chunk is self-contained content, promote to <article>. If it's a labeled subdivision of a larger article, promote to <section>. If it's the document's main content, promote to <main>. If you can't articulate what makes it more than a styling wrapper, leave it as a div.