Landmarks & page regions

Landmarks are the map of a page. When you use the right HTML element for each region — header, nav, main, aside, footer — the browser exposes a built-in set of waypoints that screen reader users can jump between directly, the way a sighted user scans a layout and goes straight to the part they want. Get the regions right and a user can press one key to skip the masthead and land in the content, list every navigation block, or move to the footer without arrowing through everything in between.

Get them wrong and that map disappears. A page with no main gives the “skip to content” key nothing to target. Three nav blocks with no names are announced as three identical “navigation” regions a user can’t tell apart. And a page assembled entirely from divs has no landmarks at all — every region looks the same to assistive technology. This lesson covers the three defects behind most landmark failures; each fix is a small change to the elements you already have.

What you’ll learn

How to give every page exactly one main landmark so “skip to content” works; how to give each nav and aside a unique accessible name with aria-label or aria-labelledby when more than one is present; and how to build the page from real header, nav, main, and footer elements instead of generic divs so the regions reach the accessibility tree.

Standards this lesson maps to
Standard Criterion Level What it requires
WCAG 2.2 1.3.1 Info and Relationships A The structure and relationships of regions are conveyed in the markup, not by visual layout alone — so landmark roles must be real, not implied.
WCAG 2.2 2.4.1 Bypass Blocks A A mechanism is available to skip blocks repeated across pages — a main landmark plus a skip link satisfies this.
WCAG 2.2 4.1.2 Name, Role, Value A Each region exposes its role and, when repeated, a distinguishing name to assistive technology.
WCAG 2.2 2.4.6 Headings and Labels AA Labels (including landmark names) describe their purpose, so repeated regions can be told apart.
EN 301 549 9.1.3.1 / 9.2.4.1 (incorporates WCAG) European harmonised standard; references the WCAG A/AA set including structure and bypass-blocks criteria.
Section 508 502.3 / 504 (incorporates WCAG A & AA) US federal ICT must meet WCAG 2.0 Level A and AA, including info-and-relationships and bypass blocks.
ADA Title II WCAG 2.1 AA (DOJ rule) AA US state/local government web content must conform to WCAG 2.1 AA, including landmark structure.

The three problems we’ll fix

Each card below isolates one common landmark defect. For every issue you get a plain-language statement of the problem, a Bad example (shown as escaped, non-running code so it can’t affect this page), a Good example, the copyable Code, and an ordered fix.

No <main> landmark

WCAG 2.2 · 1.3.1 A 2.4.1 A EN 301 549 Section 508

The <main> element marks the one region that holds the page’s primary content — the part that isn’t the header, navigation, or footer. It is the natural destination for a “skip to content” link and for the screen reader shortcut that jumps straight to the main region. Without it, a keyboard or screen reader user who wants to bypass the repeated masthead and menus has nothing to land on: the skip link points at no target, the “main” rotor command finds nothing, and they’re forced to tab through every header link on every page. A page must have exactly one <main>, and it must not be nested inside another landmark.

Bad

The primary content sits in a plain <div>. There is no main landmark, so the skip link has no target and the “jump to main content” shortcut does nothing (2.4.1).

bad-no-main.html
<a href="#content">Skip to main content</a>
<header>…site header…</header>
<div id="content">
  <h1>Quarterly report</h1>
  <p>…</p>
</div>

Good

The primary content is wrapped in a single <main> with an id the skip link targets. Focusing it (it takes tabindex="-1" so script focus works) moves the user straight past the header.

good-main.html
<a class="skip-link" href="#main">Skip to main content</a>
<header>…site header…</header>
<main id="main" tabindex="-1">
  <h1>Quarterly report</h1>
  <p>…</p>
</main>
<footer>…</footer>

Code

Use exactly one <main> per page and keep it at the top level — never inside <header>, <nav>, or another landmark. If you can’t change the element, the equivalent role is a fallback, but the native element is preferred.

main-role-fallback.html
<!-- Preferred: the native element -->
<main id="main" tabindex="-1"> … </main>

<!-- Fallback only when the element can’t be changed -->
<div role="main" id="main" tabindex="-1"> … </div>

How to fix

  1. Wrap the page’s primary content in a single <main> element.
  2. Give it an id (for example main) and point the skip link’s href at it.
  3. Add tabindex="-1" so the region can receive focus when the skip link is followed.
  4. Keep it at the top level — don’t nest <main> inside another landmark, and never use more than one per page.
  5. Test with the screen reader’s landmark list and by pressing the skip link with the keyboard.

Repeated <nav> / <aside> with no accessible name

WCAG 2.2 · 1.3.1 A 4.1.2 A 2.4.6 AA EN 301 549

A single <nav> or <aside> is fine with no name — its role already says what it is. But the moment a page has two or more of the same landmark type, they all announce identically: a screen reader’s landmark list shows “navigation, navigation, navigation” with no way to tell the primary menu from the breadcrumb or the in-page table of contents. The fix is to give each repeated region a short, unique accessible name with aria-label, or with aria-labelledby when a visible heading already names it. The name should not include the word “navigation” — the role supplies that — so “Primary”, not “Primary navigation”.

Bad

Three navigation regions, all unnamed. In the landmark list they’re three indistinguishable “navigation” entries, so the user can’t choose the one they want (1.3.1, 2.4.6).

bad-unnamed-regions.html
<nav> … primary menu … </nav>
<nav> … breadcrumb … </nav>
<aside> … related links … </aside>
<aside> … advert … </aside>

Good

Each repeated region gets a short, unique aria-label. The landmark list now reads “Primary, navigation”, “Breadcrumb, navigation”, and so on, so the user can jump straight to the one they need.

good-named-regions.html
<nav aria-label="Primary"> … </nav>
<nav aria-label="Breadcrumb"> … </nav>
<aside aria-label="Related links"> … </aside>
<aside aria-label="Advertisement"> … </aside>

Code

When a visible heading already titles the region, point at it with aria-labelledby instead of repeating the text — the name then stays in sync with what’s on screen.

labelledby-region.html
<aside aria-labelledby="toc-heading">
  <h2 id="toc-heading">On this page</h2>
  <nav aria-label="Table of contents"> … </nav>
</aside>

<!-- Avoid: redundant word "navigation" in the name -->
<nav aria-label="Primary navigation"> … </nav>

How to fix

  1. Find every place a landmark type appears more than once — usually multiple <nav> or <aside> regions.
  2. Give each one a short, unique name with aria-label, or with aria-labelledby when a visible heading already names it.
  3. Leave the role’s word out of the name — write “Primary”, not “Primary navigation”, so it isn’t announced twice.
  4. A lone landmark of its type doesn’t need a name; only add one when there’s ambiguity.
  5. Verify in the screen reader’s landmark list that every region is now distinct.

Whole page built from <div>s

WCAG 2.2 · 1.3.1 A EN 301 549 Section 508 ADA Title II

A <div> carries no meaning — it’s a generic container with no role. A page assembled entirely from <div class="header">, <div class="nav">, <div class="main"> and the like looks correct on screen but is a flat, structureless blob in the accessibility tree. There are no landmarks to list and nothing to jump between, so a screen reader user has to read or tab through the whole page in source order to find anything. The class names mean nothing to assistive technology. Swapping each generic container for the element that matches its purpose — header, nav, main, footer — restores the structure for free, with no ARIA needed and no change to how the page looks.

Bad

Every region is a <div> labelled only by a class. To assistive technology this page has no header, no navigation, and no main — just anonymous boxes (1.3.1).

bad-div-soup.html
<div class="header"> … logo … </div>
<div class="nav"> … menu links … </div>
<div class="main">
  <div class="title">Pricing</div>
  <div>…</div>
</div>
<div class="footer"> … </div>

Good

Each container becomes the element that names its role. The browser now exposes banner, navigation, main, and contentinfo landmarks automatically — no ARIA required. A real <h1> replaces the div.title.

good-semantic-regions.html
<header> … logo … </header>
<nav aria-label="Primary"> … menu links … </nav>
<main id="main" tabindex="-1">
  <h1>Pricing</h1>
  <p>…</p>
</main>
<footer> … </footer>

Code

Two caveats. A <header> or <footer> only becomes a banner/contentinfo landmark when it’s a direct child of <body> — inside an <article> it’s scoped to that article. And reach for ARIA roles only when the native element truly can’t be used.

landmark-scope.html
<!-- Direct child of body: this is the page banner -->
<body>
  <header> … </header>
</body>

<!-- Inside article: NOT a banner, scoped to the article -->
<article>
  <header> … article byline … </header>
</article>

<!-- ARIA only when the element can’t be changed -->
<div role="navigation" aria-label="Primary"> … </div>

How to fix

  1. Replace each generic container with the element that matches its purpose: <header>, <nav>, <main>, <aside>, <footer>.
  2. Keep the page-level <header> and <footer> as direct children of <body> so they register as banner and contentinfo.
  3. Use a real heading hierarchy (<h1> down) instead of styled <div>s for titles.
  4. Add role attributes only as a last resort when the native element genuinely can’t be used.
  5. Confirm with the landmark list that banner, navigation, main, and contentinfo all appear.

Recap

  • Give every page exactly one <main> wrapping the primary content, so the skip link and the “jump to main” shortcut have a target (1.3.1, 2.4.1).
  • When a page has more than one <nav> or <aside>, give each a unique name with aria-label or aria-labelledby so they’re distinguishable (1.3.1, 4.1.2, 2.4.6).
  • Build regions from the real elements — <header>, <nav>, <main>, <footer> — not generic <div>s, so landmark roles reach the accessibility tree (1.3.1).

The same fixes satisfy WCAG, EN 301 549, Section 508, and ADA Title II at once — name your regions with real elements and you meet them all.