Skip to main content

What are landmark elements?

Landmark elements are semantic HTML elements that define the structure and major regions of your web page. They help screen reader users navigate your content efficiently by allowing them to jump directly to different sections.

Common landmark elements

header

Contains introductory content, typically including navigation and branding. Maps to the banner landmark role.

nav

Contains navigation links. Screen readers can jump directly to navigation menus.

main

Contains the primary content of the page. There should only be one <main> per page.

footer

Contains footer information like copyright, links, and contact info. Maps to the contentinfo landmark role.

section

Represents a standalone section of content. Should typically have a heading and/or aria-labelledby.

aside

Contains content tangentially related to the main content, like sidebars or callouts.

article

Represents a self-contained piece of content that could stand alone (blog posts, news articles, etc.).

form

Contains form controls. Use role="search" for search forms.

Why landmarks matter

Screen reader users rely on landmarks to understand page structure and navigate efficiently. Without proper landmarks:
  • Screen reader users must listen to all content sequentially to understand page structure
  • Navigation becomes tedious and time-consuming
  • Important sections may be difficult to locate
  • The page appears as one long, unstructured flow of content
With proper landmarks:
  • Users can jump directly to navigation, main content, or footer
  • Screen readers announce regions clearly (“navigation”, “main”, etc.)
  • Users can get a quick overview of page structure
  • Navigation is faster and more efficient

Proper landmark usage

Here’s an example of proper landmark structure from the course website:
introduction.html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Web A11y for Devs | Introduction</title>
    <link rel="stylesheet" href="../styles/index.css" />
  </head>

  <body class="min-h-dvh">
    <header class="sticky top-0 z-10 py-3">
      <a href="#main-content" class="skip-link">Skip to content</a>

      <nav class="container mx-auto flex items-center justify-between gap-4">
        <h1 class="max-md:mx-auto">
          <a class="flex items-center gap-2 text-2xl" href="/">
            <img
              src="../../public/tdl-logo.jpg"
              width="30"
              height="30"
              class="inline rounded-full shadow-md"
              alt="TestDevLab"
            />
            Web A11y for Devs
          </a>
        </h1>

        <ul class="flex flex-wrap gap-2">
          <li><a href="./introduction" aria-current="page">Introduction</a></li>
        </ul>
      </nav>
    </header>

    <main id="main-content" tabindex="-1">
      <!-- Main page content goes here -->
    </main>

    <footer class="py-3 text-center font-semibold">
      <p>Web A11y for Devs, <span id="current-year"></span></p>
    </footer>
  </body>
</html>

Key features of this structure:

1

Skip link in header

The <a href="#main-content"> link allows keyboard users to skip directly to main content, bypassing repeated navigation.
<header>
  <a href="#main-content" class="skip-link">Skip to content</a>
  <!-- ... -->
</header>
2

Navigation inside header

The <nav> element is nested within <header>, clearly marking the navigation region.
<header>
  <nav>
    <!-- Navigation links -->
  </nav>
</header>
3

Main content with ID and tabindex

The <main> element has id="main-content" (for the skip link target) and tabindex="-1" (to allow programmatic focus).
<main id="main-content" tabindex="-1">
  <!-- Primary page content -->
</main>
tabindex="-1" allows JavaScript to focus the element (for skip links) without adding it to the natural tab order.
4

Footer landmark

The <footer> element contains site-wide footer information.
<footer class="py-3 text-center font-semibold">
  <p>Web A11y for Devs, <span id="current-year"></span></p>
</footer>

Using sections with labels

When using <section> elements, always provide a label using aria-labelledby to reference a heading ID:
<section class="container" aria-labelledby="screen-readers">
  <h2 id="screen-readers">Screen readers</h2>
  
  <p>
    Screen readers are hardware and software technologies that
    <b>parse the content of any application</b> and read it out loud using
    <b>text-to-speech</b>.
  </p>
  
  <!-- More content -->
</section>

Why use aria-labelledby?

Screen readers announce the section with its label: “Screen readers, region” - giving users immediate context about the content.
Labeled sections appear in screen reader landmark menus, making navigation easier.
WCAG guidelines recommend labeling all landmark regions when there are multiple landmarks of the same type.

Nested sections

You can nest sections for hierarchical content structure:
<section class="container" aria-labelledby="custom-buttons">
  <h3 id="custom-buttons">Custom buttons</h3>
  
  <p>Content about custom buttons...</p>
  
  <section class="exercise shadow-md" aria-labelledby="exercise-2">
    <h3 id="exercise-2">Exercise 2</h3>
    
    <p>
      Convert the <span class="code">div</span> element below into a fully
      functioning button element.
    </p>
  </section>
</section>

Best practices

Every page should have exactly one <main> element containing the primary content.
<!-- Good -->
<main id="main-content">
  <!-- Primary page content -->
</main>

<!-- Bad - multiple main elements -->
<main>Content 1</main>
<main>Content 2</main>
Maintain a logical heading hierarchy (h1 → h2 → h3). Don’t skip from h1 to h3.
<!-- Good -->
<h1>Page title</h1>
<h2>Section title</h2>
<h3>Subsection title</h3>

<!-- Bad - skips h2 -->
<h1>Page title</h1>
<h3>Subsection title</h3>
If you have multiple <nav> or <section> elements, label them to distinguish between them.
<nav aria-label="Primary navigation">
  <!-- Main site navigation -->
</nav>

<nav aria-label="Footer navigation">
  <!-- Footer links -->
</nav>
Prefer native HTML landmarks over ARIA roles when possible.
<!-- Good - semantic HTML -->
<nav><!-- Navigation links --></nav>

<!-- Less preferred - manual ARIA -->
<div role="navigation"><!-- Navigation links --></div>
Don’t create unnecessary div nesting. Use semantic elements to provide structure.
<!-- Good -->
<header>
  <nav><!-- Links --></nav>
</header>

<!-- Bad - meaningless divs -->
<div class="header">
  <div class="nav"><!-- Links --></div>
</div>

Common mistakes

The Broken Landmarks page intentionally demonstrates several landmark-related issues:
Issues found in broken-landmarks.html:
  1. Using <div> instead of <header> for the header section
  2. Using <div> instead of <nav> for navigation
  3. Using <span> instead of <main> for main content
  4. Using <div> instead of <footer> for footer
  5. Missing skip link or improper skip link implementation
  6. Incorrect or missing tabindex on main content

Testing landmarks

To verify your landmarks are properly implemented:
  1. Use a screen reader (NVDA, JAWS, VoiceOver) to navigate by landmarks
  2. Use browser DevTools to inspect the accessibility tree
  3. Use the HeadingsMap browser extension to visualize page structure
  4. Use axe DevTools to catch landmark issues automatically

Exercise

Ready to practice? Try the Broken Landmarks Exercise where you’ll fix all the landmark element issues in a broken page.

Resources

Build docs developers (and LLMs) love