How to Fix Missing or Incorrect Landmarks
Landmarks are regions of a page that assistive technologies expose as a navigation mechanism. Screen reader users can jump directly between landmarks, bypassing repetitive content and reaching the section they need in seconds. When landmarks are missing, duplicated, or improperly nested, this navigation breaks down and users are forced to traverse content linearly.
This guide covers every landmark-related rule flagged by axe-core, the WCAG success criteria behind them, and how to fix each issue with practical code changes.
WCAG Success Criteria
Landmark issues map to two Level A success criteria:
- SC 1.3.1 Info and Relationships (Level A) requires that information, structure, and relationships conveyed through presentation are available programmatically. Using semantic landmark elements ensures that the visual page structure is exposed to assistive technologies.
- SC 2.4.1 Bypass Blocks (Level A) requires a mechanism to bypass blocks of content that are repeated on multiple pages. Landmarks provide one such mechanism: screen reader users can skip the header and navigation by jumping directly to the
mainlandmark.
HTML5 Landmark Elements and Their ARIA Roles
HTML5 introduced semantic elements that implicitly carry ARIA landmark roles. You do not need to add role attributes when you use these elements correctly:
<header>maps tobannerwhen it is a direct child of<body>(not nested inside<article>,<aside>,<main>,<nav>, or<section>).<nav>maps tonavigation.<main>maps tomain.<aside>maps tocomplementary.<footer>maps tocontentinfowhen it is a direct child of<body>(same scoping rules as<header>).<section>maps toregiononly when it has an accessible name (viaaria-labeloraria-labelledby).<form>maps toformonly when it has an accessible name.
axe-core Rules Reference
Here is every landmark rule axe-core checks, what it means, and how to resolve it.
landmark-one-main — The page must contain exactly one main landmark. Without it, screen reader users have no way to jump to the primary content. Fix: wrap your primary content in a <main> element.
bypass — The page must provide a way to bypass repeated blocks of content. A <main> landmark satisfies this requirement. Alternatively, add a skip link as the first focusable element on the page.
region — All page content must be contained within a landmark region. Any text or interactive element outside a <header>, <nav>, <main>, <aside>, or <footer> will trigger this rule. Fix: ensure every visible element on the page lives inside an appropriate landmark.
landmark-unique — If more than one landmark of the same type exists, each must have a unique accessible name. Two <nav> elements without labels are indistinguishable in the landmarks list. Fix: add aria-label to each one.
landmark-banner-is-top-level — The banner landmark must not be nested inside another landmark. Fix: move your <header> so it is a direct child of <body>, not inside <main> or <section>.
landmark-contentinfo-is-top-level — The contentinfo landmark must not be nested inside another landmark. Fix: move your <footer> so it is a direct child of <body>.
landmark-no-duplicate-banner — The page must not have more than one banner landmark. If you have a <header> inside an <article>, it does not create a banner landmark, so this is usually caused by having two top-level <header> elements or two elements with role="banner". Fix: keep only one top-level <header>.
landmark-no-duplicate-contentinfo — The page must not have more than one contentinfo landmark. Fix: keep only one top-level <footer>.
landmark-complementary-is-top-level — The complementary landmark (<aside>) must be a top-level landmark or nested directly inside the main landmark. Fix: move <aside> out of <nav>, <header>, <footer>, or other non-main landmarks.
landmark-main-is-top-level — The main landmark must not be nested inside another landmark. Fix: ensure <main> is a direct child of <body> (or a non-landmark wrapper like a layout <div>).
Fixing a Page Without Landmarks
The most common issue is a page built entirely with <div> elements and no semantic structure. Here is a typical broken page and the corrected version.
Before — no landmarks:
<body>
<div class="header">
<div class="logo">Acme Corp</div>
<div class="nav">
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</div>
</div>
<div class="sidebar">
<div class="widget">Related links</div>
</div>
<div class="content">
<h1>Welcome</h1>
<p>Main content here.</p>
</div>
<div class="footer">
<p>© 2026 Acme Corp</p>
</div>
</body>
After — correct landmark structure:
<body>
<header>
<a href="/">Acme Corp</a>
<nav aria-label="Main">
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
</header>
<aside aria-label="Sidebar">
<p>Related links</p>
</aside>
<main>
<h1>Welcome</h1>
<p>Main content here.</p>
</main>
<footer>
<p>© 2026 Acme Corp</p>
</footer>
</body>
This structure produces the following landmarks list in a screen reader: banner, navigation "Main", complementary "Sidebar", main, contentinfo. Every element on the page is inside a landmark, satisfying the region rule.
Labeling Multiple Landmarks of the Same Type
Pages commonly have multiple <nav> or <aside> elements. Without labels, a screen reader announces them identically, making them useless as navigation targets.
<!-- Before: two unlabeled nav elements -->
<nav>
<a href="/">Home</a>
<a href="/products">Products</a>
</nav>
<nav>
<a href="/privacy">Privacy</a>
<a href="/terms">Terms</a>
</nav>
<!-- After: each nav has a unique label -->
<nav aria-label="Main">
<a href="/">Home</a>
<a href="/products">Products</a>
</nav>
<nav aria-label="Legal">
<a href="/privacy">Privacy</a>
<a href="/terms">Terms</a>
</nav>
You can also use aria-labelledby if the landmark contains a visible heading:
<aside aria-labelledby="sidebar-title">
<h2 id="sidebar-title">Related Articles</h2>
<ul>
<li><a href="/article-1">Article 1</a></li>
<li><a href="/article-2">Article 2</a></li>
</ul>
</aside>
Complete Page Structure Template
This template satisfies every axe-core landmark rule. Use it as a starting point for new pages:
<body>
<a href="#main-content" class="sr-only focus:not-sr-only">
Skip to main content
</a>
<header>
<a href="/">Site Name</a>
<nav aria-label="Main">
<ul>
<li><a href="/features">Features</a></li>
<li><a href="/pricing">Pricing</a></li>
<li><a href="/docs">Docs</a></li>
</ul>
</nav>
</header>
<main id="main-content">
<h1>Page Title</h1>
<p>Primary content.</p>
<section aria-labelledby="section-features">
<h2 id="section-features">Features</h2>
<p>Section content.</p>
</section>
<aside aria-label="Related resources">
<h2>Related Resources</h2>
<p>Sidebar content inside main.</p>
</aside>
</main>
<footer>
<nav aria-label="Footer">
<a href="/privacy">Privacy</a>
<a href="/terms">Terms</a>
</nav>
<p>© 2026 Site Name</p>
</footer>
</body>
Nested Landmarks
Some landmarks can be nested; others cannot. The rules are straightforward:
bannerandcontentinfomust be top-level. They cannot be nested insidemain,navigation,complementary, or any other landmark.mainmust be top-level. It cannot be nested inside another landmark.navigationcan be nested insidebanner,main,complementary, orcontentinfo. This is common and correct: a<nav>inside<header>is the standard pattern.complementarymust be top-level or nested directly insidemain. It cannot be nested insidenavigation,banner, orcontentinfo.region(labeled<section>) can be nested inside any landmark. Use it to subdividemaincontent into named regions.form(labeled<form>) can be nested inside any landmark. A search form in the header is a common example.
A common mistake is wrapping the entire page in a <main> element, which places <header> and <footer> inside it and breaks the top-level requirement for banner and contentinfo:
<!-- Wrong: header and footer are inside main -->
<main>
<header>...</header>
<h1>Content</h1>
<footer>...</footer>
</main>
<!-- Correct: header, main, and footer are siblings -->
<header>...</header>
<main>
<h1>Content</h1>
</main>
<footer>...</footer>
Landmarks in Single-Page Applications
SPAs present unique challenges because the page shell remains static while content is loaded dynamically into a container. The key rules apply equally:
- Maintain the same landmark structure as a traditional page:
<header>,<nav>,<main>,<footer>as siblings. - Swap content inside
<main>, not alongside it. Never remove or replace the<main>element itself during route changes. - After a route change, move focus to the
<main>element or the new page heading. This ensures screen reader users know the content has changed. - Update the page
<title>on every route change so the new context is announced. - If the SPA renders a modal dialog, the dialog itself does not need landmarks, but the underlying page structure must remain valid when the dialog is closed.
<!-- SPA shell -->
<header>
<nav aria-label="Main">...</nav>
</header>
<main id="app-content">
<!-- Route content is injected here -->
</main>
<footer>...</footer>
<script>
// After route change:
document.title = newPageTitle;
document.getElementById('app-content').focus();
</script>
For the focus call to work, add tabindex="-1" to the <main> element so it can receive programmatic focus without appearing in the tab order.
Testing with Screen Reader Landmarks
The fastest way to verify your landmark structure is to open the landmarks list in a screen reader:
- VoiceOver (macOS): Open the rotor with
VO + U, then use the left/right arrow keys to navigate to the Landmarks list. - NVDA (Windows): Press
Dto jump to the next landmark, or open the Elements List withNVDA + F7and select the Landmarks tab. - JAWS (Windows): Press
Rto jump to the next landmark, or pressInsert + Ctrl + Rto list all landmarks. - TalkBack (Android): Open the local context menu and select Landmarks.
A well-structured page should produce a landmarks list similar to this:
banner
navigation "Main"
main
region "Features"
complementary "Related resources"
contentinfo
navigation "Footer"
If you see unnamed landmarks, duplicate entries, or missing entries, go back and check the corresponding elements. You can also use browser developer tools: in Chrome, open the Accessibility panel in DevTools, expand the accessibility tree, and look for landmark roles on each element.
Automated tools like axe-core catch structural violations, but manual testing with a screen reader confirms the experience matches your intent. A page can pass all automated checks and still have a confusing landmark structure if the labels are vague or the regions do not match the visual layout.
Er din website tilgængelig?
Scan din website gratis og få din WCAG-score på få minutter.
Scan dit site gratis