DNA📄 HTMLSemantic HTML & Document Structure
ðŸĢHatchlingHTMLSemanticsAccessibility

Semantic HTML & Document Structure

Semantic HTML isn't decoration — it's the contract between your markup and every consumer of the web: browsers, screen readers, search engines, and future developers.

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:

  1. Assistive technology — Screen readers construct a navigation model from landmarks and headings, not class names
  2. Search engines — Crawlers use element semantics to weight content relevance and extract structured meaning
  3. 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 Announcement

The 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 → h4 without h3 is 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:

ElementImplicit ARIA RolePurpose
<header>banner (when top-level)Site-wide header, branding, primary navigation
<nav>navigationMajor navigation blocks
<main>mainPrimary content — one per page, no nesting
<aside>complementaryTangentially related content (sidebars, callouts)
<footer>contentinfo (when top-level)Site-wide footer, copyright, links
<section>region (when labeled)Thematic grouping of content
<article>articleSelf-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>&copy; 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:

ElementUse 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:

  1. Layout wrappers — Grid/flex containers that exist purely for styling
  2. JavaScript hooks — Elements that serve as mount points or ref targets
  3. 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 attributes

The 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>