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.1A2.4.1AEN 301 549Section 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
Wrap the page’s primary content in a single <main>
element.
Give it an id (for example main) and point the
skip link’s href at it.
Add tabindex="-1" so the region can receive focus when the
skip link is followed.
Keep it at the top level — don’t nest <main> inside
another landmark, and never use more than one per page.
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.1A4.1.2A2.4.6AAEN 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).
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.
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
Find every place a landmark type appears more than once — usually
multiple <nav> or <aside> regions.
Give each one a short, unique name with aria-label, or with
aria-labelledby when a visible heading already names it.
Leave the role’s word out of the name — write “Primary”, not “Primary
navigation”, so it isn’t announced twice.
A lone landmark of its type doesn’t need a name; only add one when there’s
ambiguity.
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.1AEN 301 549Section 508ADA 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).
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.
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
Replace each generic container with the element that matches its purpose:
<header>, <nav>,
<main>, <aside>,
<footer>.
Keep the page-level <header> and
<footer> as direct children of <body>
so they register as banner and contentinfo.
Use a real heading hierarchy (<h1> down) instead of
styled <div>s for titles.
Add role attributes only as a last resort when the native
element genuinely can’t be used.
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.