PushBackLog

Semantic HTML

Soft enforcement Complete by PushBackLog team
Topic: accessibility Topic: quality Skillset: frontend Technology: HTML Stage: execution Stage: review

Semantic HTML

Status: Complete
Category: Accessibility
Default enforcement: Soft
Author: PushBackLog team


Tags

  • Topic: accessibility, quality
  • Skillset: frontend
  • Technology: HTML
  • Stage: execution, review

Summary

Semantic HTML means using HTML elements for their intended purpose, not just their default visual appearance. Using the correct element — <nav>, <main>, <article>, <button> — communicates structure and intent to screen readers, search engines, and other tools that consume the DOM.


Rationale

HTML elements carry meaning, not just styling

Every HTML element has a default role, name, and set of expected behaviours communicated to the browser and its accessibility tree. A <button> is announced as a button by screen readers, is focusable by default, and fires on both click and Enter/Space. A <div> is announced as nothing, is not focusable, and responds only to pointer events.

When developers use <div onclick="..."> instead of <button>, they’re discarding all of that built-in accessibility behaviour and must manually reconstruct it with ARIA, tabindex, and JavaScript. The ARIA specification describes this as a last resort: “No ARIA is better than bad ARIA.”

Semantic HTML creates navigable structure

Screen reader users navigate pages by structure, not by visual layout:

  • Jump to the next heading (<h1><h6>)
  • Jump to the page <main> to skip navigation
  • Jump between <section> and <article> elements
  • Navigate a list of links within a <nav>

A page built entirely from <div> and <span> elements is a flat, undifferentiated block of text to a screen reader. Heading hierarchy and sectioning elements are the page’s skeleton.

SEO and maintainability benefit too

Search engines use semantic elements to understand page structure: <h1> signals the primary topic; <article> signals standalone content; <main> signals the primary content area. Correct semantics improve crawability. Engineers reading the code also benefit: <nav> communicates intent; <div class="nav-wrapper"> requires reading the class to understand purpose.


Guidance

Common semantic elements and when to use them

ElementUse forNOT for
<header>Site header or section header (logo, nav, h1)Arbitrary containers
<nav>Primary or secondary navigation listsAny list of links
<main>The page’s primary content (one per page)Repeated across pages
<article>Self-contained content that makes sense independentlyLayout containers
<section>Thematically-grouped content with a headingDividing layout sections without semantic purpose
<aside>Content tangentially related to surrounding contentPop-ups or modals
<footer>Site footer or section footerGeneric bottom-of-box areas
<button>Any clickable action that doesn’t navigateNavigation (use <a> instead)
<a>Navigation to another URL or page anchorClickable actions without a URL
<label>Labelling every form inputStyling text near inputs

Heading hierarchy

<!-- Wrong: headings chosen for size, not hierarchy -->
<h1>PushBackLog</h1>
<h3>Latest Sprints</h3>  <!-- Skips h2 -->
<h5>Sprint 12</h5>       <!-- Two levels skipped -->

<!-- Correct: hierarchy follows document structure -->
<h1>PushBackLog</h1>
  <h2>Latest Sprints</h2>
    <h3>Sprint 12</h3>
    <h3>Sprint 11</h3>
  <h2>Best Practices</h2>

If the visual size of a heading level is wrong, fix it with CSS, not by choosing the wrong heading level.

Interactive elements

<!-- Wrong: requires manual ARIA, tabindex, keyboard handling -->
<div
  onclick="handleDelete()"
  class="btn-delete"
  role="button"
  tabindex="0"
  onkeypress="if(event.key === 'Enter') handleDelete()"
>
  Delete
</div>

<!-- Correct: all of the above is built in -->
<button type="button" onclick="handleDelete()">Delete</button>

Examples

Page skeleton with semantic elements

<body>
  <header>
    <a href="/"><img src="/logo.svg" alt="PushBackLog" /></a>
    <nav aria-label="Main navigation">
      <ul>
        <li><a href="/library">Library</a></li>
        <li><a href="/sprints">Sprints</a></li>
      </ul>
    </nav>
  </header>

  <main>
    <h1>Best Practices Library</h1>
    <section>
      <h2>SOLID Principles</h2>
      <article>
        <h3>Single Responsibility</h3>
        <p>Every module should have one reason to change.</p>
      </article>
    </section>
  </main>

  <footer>
    <p>&copy; 2025 PushBackLog</p>
  </footer>
</body>

Anti-patterns

1. <div onclick> instead of <button>

The most common. A <div> with an onclick handler is not keyboard-focusable, not announced as a button by screen readers, and fires no events on keyboard activation. Use <button type="button"> for actions; use <a href> for navigation.

2. Skipping heading levels for stylistic reasons

Going from <h1> to <h3> destroys the logical outline that screen reader users navigate by. Visual size is a styling concern; use CSS to resize headings.

3. Using <table> for layout

Layout tables were an HTML 4 workaround. They are announced as tables by screen readers, with meaningless “cell” navigation. Use CSS Grid or Flexbox for layout.

4. Nesting interactive elements

<!-- Invalid: button inside anchor -->
<a href="/checkout"><button>Checkout</button></a>
<!-- Screen readers and browsers handle this inconsistently -->

Interactive elements should not be nested. A link that contains a button is ambiguous.



Part of the PushBackLog Best Practices Library. Suggest improvements →