Skip to main content
Saved
Pattern
Difficulty Beginner

Semantic HTML

Use correct HTML elements to convey meaning and structure before reaching for ARIA attributes.

By Den Odell Added

Semantic HTML

Problem

I once audited a site where literally everything was a div—buttons, navigation, main content, footer. Screen readers couldn’t distinguish any of it; the whole page sounded like one endless, unstructured blob.

Screen reader users navigate by landmarks like “skip to main content” and “jump to navigation,” but when everything is a div, there are no landmarks. Users have to listen through your entire page linearly, hoping to find what they need.

Those div buttons don’t work with keyboards either. Tab doesn’t focus them, Enter doesn’t click them, and screen readers don’t announce them as interactive—users don’t even know they’re clickable.

Solution

Use HTML elements for what they were designed for—it sounds obvious, but we forget constantly.

Use <button> for clickable actions, <a> for links, <nav> for navigation, <main> for primary content, and <header>/<footer> for those sections. Use <article> for standalone content, <aside> for secondary content, and <h1> through <h6> for heading hierarchy. Use <table> for tabular data (not layout) and <form> with proper <label> elements for inputs.

The beautiful thing: you get accessibility for free. A <button> announces as “button” to screen readers, responds to Enter and Space keys, and receives keyboard focus—all without a single line of JavaScript or ARIA.

Example

Here’s what proper semantic structure looks like—landmarks, headings, and interactive elements that work without extra effort.

Semantic Document Structure

<body>
  <header>
    <h1>Site Title</h1>
    <nav aria-label="Primary">
      <ul>
        <li><a href="/">Home</a></li>
        <li><a href="/about">About</a></li>
      </ul>
    </nav>
  </header>

  <main>
    <article>
      <header>
        <h2>Article Title</h2>
        <time datetime="2026-01-01">January 1, 2026</time>
      </header>
      <p>Introduction paragraph...</p>
      <h3>First Section</h3>
      <p>Section content...</p>
      <figure>
        <img src="chart.png" alt="Sales data visualization">
        <figcaption>Q4 2025 Sales Performance</figcaption>
      </figure>
      <aside>
        <h4>Related Information</h4>
        <p>Additional context...</p>
      </aside>
    </article>
  </main>

  <aside aria-label="Sidebar">
    <h2>Popular Posts</h2>
    <ul>
      <li><a href="/post-1">First Post</a></li>
    </ul>
  </aside>

  <footer>
    <p>&copy; 2026 Company Name</p>
    <nav aria-label="Footer">
      <a href="/privacy">Privacy</a>
      <a href="/terms">Terms</a>
    </nav>
  </footer>
</body>

Semantic Interactive Elements

Native elements provide keyboard and screen reader support that div-based alternatives require extensive code to replicate:

<!-- Div button requires extensive ARIA and JavaScript -->
<div role="button" tabindex="0" onclick="handleClick()"
     onkeydown="handleKeyPress(event)">Submit</div>

<!-- Semantic button works automatically -->
<button type="submit">Submit</button>

<!-- Div link breaks keyboard nav and right-click -->
<div class="link" onclick="navigate('/page')">Go to page</div>

<!-- Semantic link has full browser features -->
<a href="/page">Go to page</a>

Semantic Form Elements

Proper form markup provides automatic validation, keyboard support, and screen reader announcements:

<form>
  <label for="username">Username</label>
  <input id="username" type="text" required>

  <fieldset>
    <legend>Shipping Address</legend>
    <label for="street">Street</label>
    <input id="street" type="text">
    <label for="city">City</label>
    <input id="city" type="text">
  </fieldset>

  <button type="submit">Submit</button>
  <button type="button">Cancel</button>
</form>

Semantic Data Presentation

Tables with proper headers allow screen readers to announce cell relationships, and details/summary creates accessible disclosure widgets:

<table>
  <caption>Monthly Sales Report</caption>
  <thead>
    <tr>
      <th scope="col">Month</th>
      <th scope="col">Revenue</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th scope="row">January</th>
      <td>$50,000</td>
    </tr>
  </tbody>
</table>

<details>
  <summary>Advanced Settings</summary>
  <p>Additional configuration options...</p>
</details>

Benefits

  • Screen readers just work—they announce “navigation landmark” or “button” without any ARIA attributes.
  • SEO improves automatically; search engines understand your content structure through headings and landmarks.
  • Keyboard navigation works by default, with buttons responding to Enter and Space without custom JavaScript.
  • Reader mode and text-to-speech features parse your document structure correctly.
  • Less code to write: a <button> does in one line what a div with role="button", tabindex, and keyboard handlers takes twenty lines to achieve.
  • Self-documenting code: <nav> tells you it’s navigation, while <div class="nav-wrapper"> tells you nothing.

Tradeoffs

  • You have to actually learn HTML—there are dozens of semantic elements, and knowing when to use each takes time.
  • Default browser styles fight your designs; buttons come with borders and backgrounds you’ll need to reset.
  • Some elements have counterintuitive rules: <address> is for contact info, not physical addresses. Ask me how I learned that.
  • Heading levels must follow hierarchy rules that can conflict with visual design—you can’t jump from <h1> to <h4> just because you want smaller text.
  • CSS frameworks often assume div-based structures, and third-party components render divs you can’t change without forking.
  • Retrofitting semantic HTML into an existing div-soup codebase is painful; it’s easier to start right than fix it later.
  • Semantic correctness isn’t visible in a browser—you need accessibility audits and screen reader testing to verify you got it right.

Summary

Semantic HTML means using elements for their intended purpose—providing built-in accessibility, keyboard support, and SEO benefits without extra code. A <button> works with keyboards and screen readers automatically, while a styled <div> requires extensive JavaScript and ARIA to achieve the same result poorly. Choose the right element first, reach for ARIA only when HTML falls short, and your pages will work better for everyone.

Newsletter

A Monthly Email
from Den Odell

Behind-the-scenes thinking on frontend patterns, site updates, and more

No spam. Unsubscribe anytime.