Semantic Architecture

Building meaningful structure with HTML5 semantic elements

Semantic HTML is the foundation of BiModal Design. By choosing elements based on their meaning rather than their appearance, you create structure that both humans and agents can understand. This guide covers every semantic element you need to build accessible, agent-friendly interfaces.

Remember: Semantic HTML isn't about how it looksβ€”it's about what it means.

Quick Reference: Semantic Elements

<header>

Introductory content

<nav>

Navigation links

<main>

Primary content

<article>

Self-contained content

<section>

Thematic grouping

<aside>

Tangential content

<footer>

Footer information

<figure>

Self-contained media

<time>

Date/time information

Document Structure

Every BiModal page should follow this basic structure:

Semantic Page StructureBiModal Compliant
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Page Title</title>
</head>
<body>
  <!-- Site header with logo and main navigation -->
  <header>
    <nav aria-label="Main navigation">
      <!-- Primary site navigation -->
    </nav>
  </header>

  <!-- Main content area (only one per page) -->
  <main>
    <!-- Page-specific header -->
    <header>
      <h1>Page Title</h1>
    </header>

    <!-- Primary content -->
    <article>
      <!-- Article content -->
    </article>

    <!-- Related content -->
    <aside>
      <!-- Sidebar, related links, etc. -->
    </aside>
  </main>

  <!-- Site footer -->
  <footer>
    <!-- Copyright, links, contact info -->
  </footer>
</body>
</html>

πŸ’‘ Key Rules

  • β€’ Use only one <main> element per page
  • β€’ <header> and <footer> can be used multiple times
  • β€’ Every page needs a <h1> (and only one)
  • β€’ Nest elements based on meaning, not visual layout

Semantic Elements Deep Dive

<header>

Represents introductory content or navigational aids. Can be used for the site header, article headers, or section headers.

βœ“ Good Uses:

  • β€’ Site-wide header with logo/nav
  • β€’ Article heading and metadata
  • β€’ Section introduction

βœ— Avoid:

  • β€’ Inside <footer> or <address>
  • β€’ Nested <header> elements
<article>
  <header>
    <h2>Article Title</h2>
    <p>By <span>Author Name</span></p>
    <time datetime="2025-01-15">January 15, 2025</time>
  </header>
  <p>Article content...</p>
</article>

Contains navigation links for the site or page. Use for major navigation blocks only, not every group of links.

βœ“ Good Uses:

  • β€’ Primary site navigation
  • β€’ Table of contents
  • β€’ Pagination controls
  • β€’ Breadcrumbs

βœ— Avoid:

  • β€’ Footer links (unless major navigation)
  • β€’ Social media links
  • β€’ Every list of links
<nav aria-label="Main navigation">
  <ul>
    <li><a href="/">Home</a></li>
    <li><a href="/about">About</a></li>
    <li><a href="/products">Products</a></li>
    <li><a href="/contact">Contact</a></li>
  </ul>
</nav>

<!-- Multiple nav elements need labels -->
<nav aria-label="Breadcrumb">
  <ol>
    <li><a href="/">Home</a></li>
    <li><a href="/category">Category</a></li>
    <li>Current Page</li>
  </ol>
</nav>

⚠ Important: When using multiple <nav> elements, always add aria-label to distinguish them for screen readers and agents.

<main>

Represents the dominant content of the document. There should be only one <main> element per page, and it should not be nested inside <article>, <aside>, <footer>, <header>, or <nav>.

<body>
  <header>
    <!-- Site header -->
  </header>

  <main>
    <!-- All unique page content goes here -->
    <h1>Page Title</h1>
    <article>
      <!-- Article content -->
    </article>
  </main>

  <footer>
    <!-- Site footer -->
  </footer>
</body>

βœ— Common Mistake: Using multiple <main> elements or omitting it entirely. Every page needs exactly one.

<article>

Represents a self-contained composition that could be independently distributed or reused. Use the "syndication test": could this be published elsewhere and still make sense?

βœ“ Good Uses:

  • β€’ Blog posts
  • β€’ News articles
  • β€’ Forum posts
  • β€’ Product cards
  • β€’ User comments

βœ— Avoid:

  • β€’ Navigation menus
  • β€’ Site headers/footers
  • β€’ Generic containers
<article itemScope itemType="https://schema.org/BlogPosting">
  <header>
    <h2 itemProp="headline">How to Build BiModal Sites</h2>
    <time itemProp="datePublished" datetime="2025-01-15">
      January 15, 2025
    </time>
  </header>

  <div itemProp="articleBody">
    <p>Content of the article...</p>
  </div>

  <footer>
    <p>Tags: <a href="/tag/web">Web Development</a></p>
  </footer>
</article>

<section>

Represents a thematic grouping of content, typically with a heading. If you can't think of a good heading for it, it's probably not a section.

<article>
  <h1>Complete Guide to BiModal Design</h1>

  <section>
    <h2>Introduction</h2>
    <p>What is BiModal Design...</p>
  </section>

  <section>
    <h2>Core Principles</h2>
    <p>The three pillars of BiModal...</p>
  </section>

  <section>
    <h2>Implementation</h2>
    <p>How to build BiModal sites...</p>
  </section>
</article>

πŸ’‘ Section vs Div

Use <section> for thematic content with a heading. Use <div> for styling or layout with no semantic meaning.

<aside>

Content that is tangentially related to the main content. Could be removed without affecting the primary message.

βœ“ Good Uses:

  • β€’ Sidebars
  • β€’ Pull quotes
  • β€’ Related links
  • β€’ Advertisements
  • β€’ Author bio

βœ— Avoid:

  • β€’ Primary content
  • β€’ Main navigation
  • β€’ Critical information
<article>
  <h1>Main Article Title</h1>
  <p>Main article content...</p>

  <aside>
    <h2>Related Articles</h2>
    <ul>
      <li><a href="/article-1">Related Article 1</a></li>
      <li><a href="/article-2">Related Article 2</a></li>
    </ul>
  </aside>
</article>

Represents footer information for its nearest sectioning content or root element. Can contain author info, copyright, related links, etc.

<!-- Site footer -->
<footer>
  <p>&copy; 2025 Company Name. All rights reserved.</p>
  <nav aria-label="Footer navigation">
    <ul>
      <li><a href="/privacy">Privacy</a></li>
      <li><a href="/terms">Terms</a></li>
    </ul>
  </nav>
</footer>

<!-- Article footer -->
<article>
  <h2>Article Title</h2>
  <p>Article content...</p>

  <footer>
    <p>Published by <a href="/author">Author Name</a></p>
    <p>Last updated: <time datetime="2025-01-15">Jan 15, 2025</time></p>
  </footer>
</article>

Heading Hierarchy

Proper heading structure is critical for both accessibility and agent understanding. Headings create a document outline that agents use to navigate your content.

⚠ Critical Rules

  • β€’ Every page must have exactly one <h1>
  • β€’ Never skip heading levels (h1 β†’ h3 is wrong)
  • β€’ Headings should be nested logically, not styled for size
  • β€’ Use headings for structure, CSS for appearance

βœ— Wrong Hierarchy

<h1>Page Title</h1>
<h3>Skipped h2!</h3>
<h2>Out of order</h2>
<h1>Second h1 - wrong!</h1>
<h4>What am I under?</h4>

βœ“ Correct Hierarchy

<h1>Page Title</h1>
<h2>Main Section</h2>
<h3>Subsection</h3>
<h3>Another Subsection</h3>
<h2>Another Main Section</h2>
<h3>Its Subsection</h3>

πŸ’‘ Pro Tip: Think Outline, Not Style

If your headings don't make sense in an outline view, they're wrong. Use CSS to style heading sizesβ€”never choose a heading level for its default appearance.

1. Page Title (h1)
1.1 Main Section (h2)
1.1.1 Subsection (h3)
1.1.2 Subsection (h3)
1.2 Main Section (h2)

Semantic Patterns for Common UI

<nav aria-label="Main navigation">
  <ul>
    <li><a href="/" aria-current="page">Home</a></li>
    <li><a href="/products">Products</a></li>
    <li>
      <button aria-expanded="false" aria-controls="submenu">
        Services
      </button>
      <ul id="submenu" hidden>
        <li><a href="/services/consulting">Consulting</a></li>
        <li><a href="/services/training">Training</a></li>
      </ul>
    </li>
    <li><a href="/contact">Contact</a></li>
  </ul>
</nav>

Blog Post

<article itemScope itemType="https://schema.org/BlogPosting">
  <header>
    <h1 itemProp="headline">Article Title</h1>
    <p>
      By <span itemProp="author" itemScope itemType="https://schema.org/Person">
        <span itemProp="name">Author Name</span>
      </span>
    </p>
    <time itemProp="datePublished" datetime="2025-01-15">
      January 15, 2025
    </time>
  </header>

  <div itemProp="articleBody">
    <p>Article content here...</p>

    <figure>
      <img src="image.jpg" alt="Description" itemProp="image" />
      <figcaption>Image caption</figcaption>
    </figure>

    <p>More content...</p>
  </div>

  <footer>
    <p>Tags:
      <a href="/tag/web">Web Development</a>,
      <a href="/tag/accessibility">Accessibility</a>
    </p>
  </footer>
</article>

Product Card

<article itemScope itemType="https://schema.org/Product">
  <h2 itemProp="name">Product Name</h2>

  <figure>
    <img
      src="product.jpg"
      alt="Product Name"
      itemProp="image"
    />
  </figure>

  <p itemProp="description">
    Product description goes here...
  </p>

  <div itemProp="offers" itemScope itemType="https://schema.org/Offer">
    <data itemProp="price" value="29.99">$29.99</data>
    <meta itemProp="priceCurrency" content="USD" />
    <link itemProp="availability" href="https://schema.org/InStock" />
  </div>

  <form action="/cart" method="POST">
    <input type="hidden" name="product_id" value="123" />
    <button type="submit">Add to Cart</button>
  </form>
</article>

Contact Form

<section>
  <h2>Contact Us</h2>

  <form action="/submit" method="POST">
    <div>
      <label for="name">Name</label>
      <input
        type="text"
        id="name"
        name="name"
        required
        aria-required="true"
      />
    </div>

    <div>
      <label for="email">Email</label>
      <input
        type="email"
        id="email"
        name="email"
        required
        aria-required="true"
      />
    </div>

    <div>
      <label for="message">Message</label>
      <textarea
        id="message"
        name="message"
        required
        aria-required="true"
      ></textarea>
    </div>

    <button type="submit">Send Message</button>
  </form>
</section>

Anti-Patterns to Avoid

1. Div Soup

Using <div> for everything creates meaningless structure that agents can't understand.

❌ Bad

<div class="header">
  <div class="nav">
    <div class="link">Home</div>
    <div class="link">About</div>
  </div>
</div>
<div class="content">
  <div class="post">
    <div class="title">Title</div>
    <div class="body">Content</div>
  </div>
</div>

βœ… Good

<header>
  <nav>
    <a href="/">Home</a>
    <a href="/about">About</a>
  </nav>
</header>
<main>
  <article>
    <h1>Title</h1>
    <p>Content</p>
  </article>
</main>

2. Wrong Element for the Job

Choosing elements based on appearance instead of meaning.

βœ—
<div onClick=...>Click me</div>

Use <button> for clickable actions

βœ—
<span class="big-heading">Title</span>

Use proper heading tags

βœ—
<div><div><div>Item</div></div></div>

Use <ul> and <li> for lists

3. Broken Heading Hierarchy

Skipping levels or using headings for styling breaks document structure.

βœ—
<h1>Title</h1> <h4>Skipped h2 and h3</h4>

Framework-Specific Examples

React / Next.js

// components/ProductCard.tsx
export function ProductCard({ product }) {
  return (
    <article itemScope itemType="https://schema.org/Product">
      <header>
        <h2 itemProp="name">{product.name}</h2>
      </header>

      <figure>
        <img
          src={product.image}
          alt={product.name}
          itemProp="image"
        />
      </figure>

      <p itemProp="description">{product.description}</p>

      <div itemProp="offers" itemScope itemType="https://schema.org/Offer">
        <data itemProp="price" value={product.price}>
          ${product.price}
        </data>
      </div>

      <button onClick={() => addToCart(product.id)}>
        Add to Cart
      </button>
    </article>
  );
}

Vue / Nuxt

<!-- components/ProductCard.vue -->
<template>
  <article itemscope itemtype="https://schema.org/Product">
    <header>
      <h2 itemprop="name">{{ product.name }}</h2>
    </header>

    <figure>
      <img
        :src="product.image"
        :alt="product.name"
        itemprop="image"
      />
    </figure>

    <p itemprop="description">{{ product.description }}</p>

    <div itemprop="offers" itemscope itemtype="https://schema.org/Offer">
      <data itemprop="price" :value="product.price">
        ${{ product.price }}
      </data>
    </div>

    <button @click="addToCart(product.id)">
      Add to Cart
    </button>
  </article>
</template>

Testing Your Semantic Structure

1. HTML Validator

Use the W3C HTML Validator to check for structural errors:

https://validator.w3.org/

2. Browser DevTools

Inspect the Accessibility Tree in Chrome/Firefox DevTools to see how assistive technologies and agents understand your structure.

3. Headings Map

Use browser extensions like "HeadingsMap" to visualize your heading hierarchy.

4. The Curl Test

curl -s https://yoursite.com | grep -E '<(header|nav|main|article|section|aside|footer)>'

Verify that semantic elements appear in the HTML source.

Next Steps

You now understand how to build semantic HTML structure. Next, learn how to enhance it with ARIA attributes for even better accessibility and agent comprehension.

Continue to ARIA Implementation β†’