Semantic HTML & Document Structure
A div with a click handler is not a button. A span with bold text is not a heading. Senior engineers understand that HTML elements carry meaning â and that meaning propagates through accessibility trees, search engine crawlers, and browser heuristics. Choosing the right element is an architectural decision.
Why Semantics Matter
Semantic HTML serves three audiences simultaneously:
- Assistive technology â Screen readers construct a navigation model from landmarks and headings, not class names
- Search engines â Crawlers use element semantics to weight content relevance and extract structured meaning
- Developers â Semantic markup is self-documenting;
<nav>tells you more than<div class="nav-wrapper">
Semantic Element â Implicit ARIA Role â Accessibility Tree Node â Screen Reader AnnouncementThe browser does enormous work for free â if you use the right elements.
Document Outline & Heading Hierarchy
The document outline algorithm (though inconsistently implemented) establishes content hierarchy through headings:
<body>
<h1>Application</h1> <!-- One h1 per page -->
<main>
<article>
<h2>Feature Overview</h2>
<section>
<h3>Installation</h3>
<h4>Prerequisites</h4>
</section>
<section>
<h3>Configuration</h3>
</section>
</article>
</main>
</body>Rules senior engineers follow:
- One
<h1>per page â it's the page title - Never skip heading levels (
h2âh4withouth3is a violation) - Headings should be nestable â if you removed all non-heading content, the headings alone should form a readable table of contents
Landmark Elements & Their ARIA Roles
Every landmark element maps to an implicit ARIA role. When you use the semantic element, you get the role for free:
| Element | Implicit ARIA Role | Purpose |
|---|---|---|
<header> | banner (when top-level) | Site-wide header, branding, primary navigation |
<nav> | navigation | Major navigation blocks |
<main> | main | Primary content â one per page, no nesting |
<aside> | complementary | Tangentially related content (sidebars, callouts) |
<footer> | contentinfo (when top-level) | Site-wide footer, copyright, links |
<section> | region (when labeled) | Thematic grouping of content |
<article> | article | Self-contained, independently distributable content |
<form> | form (when labeled) | User input collection |
<body>
<header> <!-- banner -->
<nav aria-label="Main"> <!-- navigation -->
<ul>...</ul>
</nav>
</header>
<main> <!-- main -->
<article> <!-- article -->
<section> <!-- region (if labeled) -->
<h2>Section Title</h2>
<p>Content...</p>
</section>
</article>
<aside> <!-- complementary -->
<h2>Related Links</h2>
</aside>
</main>
<footer> <!-- contentinfo -->
<p>© 2025</p>
</footer>
</body>Screen readers expose landmarks as a navigation menu. Users can jump directly between them â that's why <main> matters more than you think.
Content Models
HTML5 elements are categorized by what they can contain and where they can appear:
Content Models
âââ Flow Content (most elements: div, p, section, article...)
âââ Phrasing Content (inline-level: span, a, em, strong, code...)
âââ Interactive Content (clickable/focusable: a, button, input, select...)
âââ Sectioning Content (creates outline sections: article, aside, nav, section)
âââ Heading Content (h1âh6, hgroup)
âââ Embedded Content (external resources: img, video, iframe, canvas...)
âââ Metadata Content (head-level: title, meta, link, script, style)Why this matters in interviews:
- A
<p>cannot contain a<div>(block in inline = invalid nesting) - An
<a>cannot contain another<a>(interactive in interactive) - A
<button>cannot contain an<a>(same reason) - Understanding content models prevents invisible layout bugs
<!-- â Invalid: div (flow) inside p (phrasing only) -->
<p>Text <div>block</div> more text</p>
<!-- Browser auto-closes <p> before <div>, creating TWO paragraphs + orphaned div -->
<!-- â
Valid: use span for inline grouping -->
<p>Text <span>inline</span> more text</p>section vs article vs div
This is one of the most commonly confused distinctions:
| Element | Use When... | Mental Model |
|---|---|---|
<article> | Content is self-contained and independently meaningful. Could be syndicated (RSS, embed, card). | "Would this make sense in an RSS feed?" |
<section> | Content is a thematic grouping that needs a heading. Part of a larger whole. | "Is this a chapter in the document?" |
<div> | No semantic meaning needed. Pure styling/layout wrapper. | "I just need a box." |
<!-- Blog page structure -->
<main>
<section> <!-- Thematic grouping: "Latest Posts" -->
<h2>Latest Posts</h2>
<article> <!-- Self-contained: one blog post -->
<h3>Understanding Closures</h3>
<p>A closure is formed when...</p>
</article>
<article> <!-- Another self-contained post -->
<h3>Event Loop Explained</h3>
<p>The event loop is...</p>
</article>
</section>
<section> <!-- Thematic grouping: "About" -->
<h2>About This Blog</h2>
<p>Written by engineers, for engineers.</p>
</section>
</main>Key insight: <article> can contain <section>, and <section> can contain <article>. A blog post (<article>) can have sections; a section of "recent posts" can contain articles.
Implicit ARIA Roles & The role Attribute
Every semantic element carries an implicit ARIA role. The role attribute should only be used when you cannot use the native element:
<!-- â
Preferred: native element carries implicit role -->
<nav aria-label="Primary">...</nav>
<!-- â ïļ Only when you truly can't use <nav> -->
<div role="navigation" aria-label="Primary">...</div>The first rule of ARIA: Don't use ARIA if a native HTML element provides the same semantics.
Common implicit mappings engineers should know:
// Mental model: element â role
const implicitRoles = {
'a[href]': 'link',
'button': 'button',
'input[type=checkbox]': 'checkbox',
'input[type=radio]': 'radio',
'input[type=text]': 'textbox',
'select': 'combobox', // or listbox, depending on size
'textarea': 'textbox',
'img[alt]': 'img',
'img[alt=""]': 'presentation', // decorative image
'table': 'table',
'ul/ol': 'list',
'li': 'listitem',
};When to Use div
div is not wrong â it's semantically neutral. Use it when:
- Layout wrappers â Grid/flex containers that exist purely for styling
- JavaScript hooks â Elements that serve as mount points or ref targets
- No semantic meaning exists â The content doesn't map to any HTML element
<!-- â
div for layout: no semantic meaning needed -->
<div class="grid grid-cols-3 gap-4">
<article>...</article>
<article>...</article>
<article>...</article>
</div>
<!-- â div when semantics exist: use the real element -->
<div class="navigation">...</div> <!-- Should be <nav> -->
<div class="main-content">...</div> <!-- Should be <main> -->
<div onclick="submit()">Save</div> <!-- Should be <button> -->Microdata: itemscope & itemprop
Microdata embeds machine-readable data directly in HTML using Schema.org vocabulary:
<article itemscope itemtype="https://schema.org/Article">
<h2 itemprop="headline">Understanding Semantic HTML</h2>
<p itemprop="description">A deep dive into document structure...</p>
<span itemprop="author" itemscope itemtype="https://schema.org/Person">
<span itemprop="name">Alex Chen</span>
</span>
<time itemprop="datePublished" datetime="2025-01-15">Jan 15, 2025</time>
</article>This produces structured data that search engines extract for rich snippets. The alternative (and generally preferred) approach is JSON-LD in a <script> tag, but microdata has the advantage of being co-located with the visible content.
Interview Mental Model
When asked about semantic HTML, frame your answer around the three layers:
Layer 1: Structure â Document outline (headings, landmarks)
Layer 2: Meaning â Element semantics (article, nav, time, address)
Layer 3: Machine Data â Microdata / JSON-LD / ARIA attributesThe strongest answer shows awareness that semantic HTML is not about "using the right tag" â it's about understanding that your markup is an API consumed by browsers, assistive technology, search engines, and other developers. Every element choice is a declaration of intent.
Common Interview Questions
"What's the difference between <strong> and <b>?"
<strong> indicates semantic importance â screen readers may emphasize it. <b> is purely visual (bold styling) with no semantic weight. Same distinction applies to <em> (semantic emphasis) vs <i> (visual italic, or alternate voice/mood).
"Can you have multiple <header> elements?"
Yes. A <header> inside <article> or <section> scopes to that section. Only a top-level <header> (direct child of <body>) gets the banner role.
"Why does <main> matter?"
Screen reader users can press a shortcut to jump directly to <main>, bypassing all navigation. Without it, they must tab through every header link on every page load.
<!-- Accessibility shortcut: screen reader jumps here -->
<main id="main-content">
<!-- All primary content -->
</main>"What's <figure> for?"
Self-contained content (images, diagrams, code snippets) with an optional caption. The <figcaption> provides an accessible name for the figure:
<figure>
<img src="architecture.png" alt="System architecture showing three microservices" />
<figcaption>Fig. 1: Production architecture as of Q1 2025</figcaption>
</figure>