---
name: html-semantic
description: "Semantic HTML5: elements, forms with validation and accessibility, SEO best practices, structured data, Open Graph, Twitter Cards. Use when writing or reviewing markup, forms or document structure for semantics, accessibility and SEO."
tags: [html, semantic, seo, forms, open-graph, structured-data, frontend]
version: "2025.1"
---

# Semantic HTML5

## Core Principle

Use the correct HTML element for the content's meaning, not its appearance. Semantic HTML
improves accessibility, SEO, and maintainability. Style with CSS, not with HTML elements.

## Document Structure

```html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Page Title  -  Site Name</title>
  <meta name="description" content="Concise description of the page (150-160 chars)" />
  <link rel="canonical" href="https://example.com/page" />
  <link rel="icon" href="/favicon.svg" type="image/svg+xml" />
</head>
<body>
  <a href="#main-content" class="skip-link">Skip to main content</a>

  <header>
    <nav aria-label="Primary">
      <ul>
        <li><a href="/" aria-current="page">Home</a></li>
        <li><a href="/products">Products</a></li>
        <li><a href="/about">About</a></li>
        <li><a href="/contact">Contact</a></li>
      </ul>
    </nav>
  </header>

  <main id="main-content">
    <article>
      <header>
        <h1>Article Title</h1>
        <p>
          By <address class="inline"><a href="/authors/jane">Jane Doe</a></address>
          <time datetime="2025-01-15">January 15, 2025</time>
        </p>
      </header>

      <section aria-labelledby="intro-heading">
        <h2 id="intro-heading">Introduction</h2>
        <p>Content paragraph...</p>
        <figure>
          <img src="diagram.webp" alt="System architecture showing three services" width="800" height="500" />
          <figcaption>Figure 1: System architecture overview</figcaption>
        </figure>
      </section>

      <section aria-labelledby="details-heading">
        <h2 id="details-heading">Details</h2>
        <p>More content...</p>
        <blockquote cite="https://source.com/quote">
          <p>A meaningful quote from a reference.</p>
          <footer> -  <cite>Author Name</cite></footer>
        </blockquote>
      </section>

      <footer>
        <p>Tags:
          <a href="/tags/html" rel="tag">HTML</a>,
          <a href="/tags/semantic" rel="tag">Semantic</a>
        </p>
      </footer>
    </article>

    <aside aria-label="Related articles">
      <h2>Related Articles</h2>
      <ul>
        <li><a href="/related-1">Related article 1</a></li>
        <li><a href="/related-2">Related article 2</a></li>
      </ul>
    </aside>
  </main>

  <footer>
    <nav aria-label="Footer">
      <ul>
        <li><a href="/privacy">Privacy Policy</a></li>
        <li><a href="/terms">Terms of Service</a></li>
      </ul>
    </nav>
    <p>&copy; <time datetime="2025">2025</time> Company Name</p>
  </footer>
</body>
</html>
```

## Semantic Element Reference

| Element | Use For |
|---------|---------|
| `<header>` | Introductory content for page or section |
| `<nav>` | Major navigation blocks (add `aria-label` for multiple navs) |
| `<main>` | Primary content of the page (only one per page) |
| `<article>` | Self-contained, independently distributable content |
| `<section>` | Thematic grouping with a heading |
| `<aside>` | Tangentially related content (sidebar, callout) |
| `<footer>` | Footer for page or section |
| `<figure>` / `<figcaption>` | Illustrations, diagrams, code listings with caption |
| `<details>` / `<summary>` | Expandable/collapsible disclosure widget |
| `<dialog>` | Modal or non-modal dialog box |
| `<time>` | Machine-readable date/time |
| `<address>` | Contact information for nearest article or body |
| `<mark>` | Highlighted or referenced text |
| `<abbr>` | Abbreviation with expansion (`title` attribute) |

## Forms: Validation, Accessibility, Input Types

```html
<form action="/api/contact" method="POST" novalidate aria-label="Contact form">
  <!-- Text input with validation -->
  <div class="field">
    <label for="fullname">
      Full Name <span aria-hidden="true">*</span>
    </label>
    <input
      type="text"
      id="fullname"
      name="fullname"
      required
      minlength="2"
      maxlength="100"
      autocomplete="name"
      aria-required="true"
    />
  </div>

  <!-- Email with pattern -->
  <div class="field">
    <label for="email">Email <span aria-hidden="true">*</span></label>
    <input
      type="email"
      id="email"
      name="email"
      required
      autocomplete="email"
      aria-required="true"
      aria-describedby="email-help"
    />
    <p id="email-help" class="help-text">We'll only use this to reply.</p>
  </div>

  <!-- Phone -->
  <div class="field">
    <label for="phone">Phone</label>
    <input type="tel" id="phone" name="phone" autocomplete="tel" />
  </div>

  <!-- Date picker -->
  <div class="field">
    <label for="dob">Date of Birth</label>
    <input type="date" id="dob" name="dob" min="1900-01-01" max="2010-12-31" />
  </div>

  <!-- Select -->
  <div class="field">
    <label for="subject">Subject <span aria-hidden="true">*</span></label>
    <select id="subject" name="subject" required aria-required="true">
      <option value=""> -  Choose a subject  - </option>
      <option value="support">Technical Support</option>
      <option value="billing">Billing</option>
      <option value="other">Other</option>
    </select>
  </div>

  <!-- Textarea -->
  <div class="field">
    <label for="message">Message <span aria-hidden="true">*</span></label>
    <textarea
      id="message"
      name="message"
      required
      minlength="10"
      maxlength="2000"
      rows="5"
      aria-required="true"
    ></textarea>
  </div>

  <!-- Checkbox -->
  <div class="field">
    <label>
      <input type="checkbox" name="terms" required aria-required="true" />
      I agree to the <a href="/terms">Terms of Service</a>
    </label>
  </div>

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

## SEO Best Practices

```html
<head>
  <!-- Essential meta -->
  <title>Primary Keyword  -  Brand Name</title>
  <meta name="description" content="Compelling description with primary keywords (150-160 chars)" />
  <meta name="robots" content="index, follow" />
  <link rel="canonical" href="https://example.com/current-page" />

  <!-- Open Graph (Facebook, LinkedIn) -->
  <meta property="og:type" content="article" />
  <meta property="og:title" content="Article Title" />
  <meta property="og:description" content="Compelling description for social sharing" />
  <meta property="og:image" content="https://example.com/og-image.jpg" />
  <meta property="og:image:width" content="1200" />
  <meta property="og:image:height" content="630" />
  <meta property="og:url" content="https://example.com/article" />
  <meta property="og:site_name" content="Site Name" />

  <!-- Twitter Cards -->
  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:site" content="@sitehandle" />
  <meta name="twitter:title" content="Article Title" />
  <meta name="twitter:description" content="Description for Twitter" />
  <meta name="twitter:image" content="https://example.com/twitter-card.jpg" />

  <!-- Structured Data (JSON-LD) -->
  <script type="application/ld+json">
  {
    "@context": "https://schema.org",
    "@type": "Article",
    "headline": "Article Title",
    "author": {
      "@type": "Person",
      "name": "Jane Doe",
      "url": "https://example.com/authors/jane"
    },
    "datePublished": "2025-01-15",
    "dateModified": "2025-01-20",
    "image": "https://example.com/article-image.jpg",
    "publisher": {
      "@type": "Organization",
      "name": "Site Name",
      "logo": { "@type": "ImageObject", "url": "https://example.com/logo.png" }
    }
  }
  </script>
</head>
```

### Heading Hierarchy

```html
<!-- Correct: one h1, logical nesting -->
<h1>Page Title</h1>
  <h2>Section A</h2>
    <h3>Subsection A.1</h3>
    <h3>Subsection A.2</h3>
  <h2>Section B</h2>
    <h3>Subsection B.1</h3>

<!-- WRONG: skipped levels, multiple h1s -->
<h1>Title</h1>
<h1>Another Title</h1>  <!-- Bad: two h1s -->
<h4>Jumped from h1 to h4</h4>  <!-- Bad: skipped h2, h3 -->
```

## Do's

- Use one `<h1>` per page matching the page title
- Use `<button>` for actions and `<a>` for navigation
- Include `alt` text on all informational images; use `alt=""` for decorative images
- Use `<time>` with `datetime` attribute for dates
- Include structured data (JSON-LD) for rich search results
- Use `autocomplete` attributes on form inputs
- Set `lang` attribute on `<html>` and on elements with different languages
- Use `<dialog>` element for modals instead of custom div implementations

## Don'ts

- Do not use `<div>` or `<span>` where a semantic element exists
- Do not use `<br>` for spacing; use CSS margin/padding
- Do not use `<b>` or `<i>` for emphasis; use `<strong>` and `<em>`
- Do not skip heading levels (h1 -> h3 without h2)
- Do not use tables for layout; use CSS Grid/Flexbox
- Do not use `onclick` on non-interactive elements; use `<button>`
- Do not leave form inputs without labels
- Do not duplicate the same `id` on a page

## Troubleshooting

| Problem | Cause | Solution |
|---------|-------|----------|
| Poor SEO ranking | Missing meta, no structured data | Add title, description, canonical, JSON-LD |
| Social shares show wrong image | Missing or incorrect OG tags | Add `og:image` with absolute URL, 1200x630px |
| Form not submitting | Missing `name` attribute on inputs | Add `name` to every form input |
| Accessibility audit fails | Divs used as buttons, missing labels | Replace with semantic elements, add labels |
| Heading outline broken | Skipped heading levels | Fix hierarchy: h1 > h2 > h3 sequentially |
