ESC

Type to search the knowledge base.

Landmark Elements

header, nav, main, aside, footer landmarks — how AT users jump sections, labeling multiple navs, and common landmark mistakes.

beginner3 min read
  • html
  • landmarks
  • a11y

Landmark elements map to regions in the accessibility tree so users can skip around a page without tabbing every link. HTML gives you most landmarks for free via semantic tags.

Docs: MDN sections and outlines, WAI page structure, ARIA landmarks.

Core landmarks

Element Typical landmark role Notes
<header> banner (when not nested in sectioning content) Site or page header
<nav> navigation Label if more than one
<main> main Exactly one visible main
<aside> complementary Sidebars, related
<footer> contentinfo (top-level) Site footer
<form> form (when named) Needs accessible name to be a landmark
<section> region (when named) Otherwise weak/no landmark
<body>
  <a class="skip-link" href="#main">Skip to content</a>
  <header>
    <p class="logo">Frontend Beauty</p>
    <nav aria-label="Primary">
      <ul>…</ul>
    </nav>
  </header>
  <main id="main">
    <h1>…</h1>
    …
  </main>
  <aside aria-label="Related articles">…</aside>
  <footer>…</footer>
</body>

Labeling multiple navs

<nav aria-label="Primary">…</nav>
<nav aria-label="Footer">…</nav>
<nav aria-labelledby="on-this-page-title">
  <h2 id="on-this-page-title">On this page</h2>
  …
</nav>

Unlabeled multiple navs all announce as “navigation” — users can’t distinguish them.

One main

<!-- Layout component mistake -->
<main>{children}</main>
<!-- nested route also wraps main → two mains -->

Enforce a single <main> in the root layout. Nested pages contribute content inside it.

section vs region

<section aria-labelledby="pricing-heading">
  <h2 id="pricing-heading">Pricing</h2>
  …
</section>

A bare <section> without a name often does not expose a useful landmark. Prefer headings + labeled sections when you want jump targets.

Landmarks help AT users with landmark navigation; keyboard users without SR still need a skip link to bypass repetitive header chrome.

Common mistakes

  1. div soup with CSS grid that looks like a layout but has no landmarks.
  2. Multiple banners from nested headers.
  3. Footer inside another contentinfo nesting confusion.
  4. Using role="main" on a div when <main> exists — redundant or conflicting.
  5. Labeling every section “region” until the landmark list is noise.

Interview out-loud

“Landmarks come from elements like header, nav, main, aside, footer. I use one main, label multiple navs, and give forms/sections names when they should appear as regions. Skip links complement landmarks for keyboard users. I avoid fake layouts made only of divs.”

Footguns

  1. Two mains from nested layouts.
  2. Unlabeled nav clusters.
  3. aside for unrelated marketing popovers (wrong semantics).
  4. Overusing complementary landmarks.
  5. Hiding main content off-screen but leaving it in the tree without care.

App shell example

<body>
  <a class="skip-link" href="#main">Skip to content</a>
  <header>…logo…</header>
  <div class="shell">
    <nav aria-label="Product">…</nav>
    <main id="main">{children}</main>
    <aside aria-label="Help">{help}</aside>
  </div>
  <footer>…</footer>
</body>

The flex/grid wrapper div.shell is layout-only — correct. Landmarks remain the semantic children. Avoid putting role="navigation" on the wrapper and another nav inside (duplicate).

Landmark noise

Too many landmarks make the rotor list useless. Do not mark every card as a region. Reserve landmarks for major page areas. Name regions only when the name helps disambiguate. A single well-structured main with good headings often beats a dozen role="region" wrappers.

Further reading

Related guides