Open a page with a screen reader and press D in NVDA (or open VoiceOver’s rotor and pick “Landmarks”) and you get a list like “banner, navigation, main, complementary, content info”. Each entry jumps you to a part of the page. It’s the screen reader equivalent of glancing at a layout and seeing “header across the top, menu on the left, article in the middle”. Without landmarks, the only way to reach the article is to read or tab through everything before it.
Landmarks come from HTML elements you probably already use, which is why this topic is a beginner-level interview staple: the question is less “what’s a landmark?” and more “which element maps to which landmark, and when doesn’t it?”
The elements and the roles they create
| Element | Landmark role | Condition |
|---|---|---|
<header> |
banner | Only when it’s not inside <article>, <aside>, <main>, <nav> or <section> |
<nav> |
navigation | Always |
<main> |
main | Always; one visible per page |
<aside> |
complementary | When not nested inside sectioning content (browser handling varies), or when labelled |
<footer> |
contentinfo | Same rule as <header> |
<section> |
region | Only when it has an accessible name |
<form> |
form | Only when it has an accessible name |
<search> |
search | Always (newer element; <form role="search"> before it) |
The conditions are where people slip. A <header> inside an <article> is just a header for that article, not the site banner, so a blog listing with ten <article> elements, each with its own <header>, doesn’t produce eleven banners. That’s by design: only the page-level header is the banner.
Rebuilding a page made of divs
Here’s a typical structure before any semantics:
<div class="top">
<div class="logo">…</div>
<div class="menu">…links…</div>
<div class="search">…form…</div>
</div>
<div class="crumbs">Home › Notes › CSS Grid</div>
<div class="content">
<div class="post">…article…</div>
<div class="sidebar">…related links…</div>
</div>
<div class="bottom">…copyright, links…</div>To a screen reader this page has no landmarks at all. The landmarks list is empty, and a user has to arrow through the logo, every menu link and the breadcrumb before reaching the article.
The rebuilt version:
<header>
<a href="/" class="logo"><img src="/logo.svg" alt="Stacktrick home"></a>
<nav aria-label="Primary">
<ul>
<li><a href="/notes/">Notes</a></li>
<li><a href="/practice/">Practice</a></li>
</ul>
</nav>
<search>
<form action="/search">
<label for="q">Search notes</label>
<input id="q" name="q" type="search">
</form>
</search>
</header>
<nav aria-label="Breadcrumb">
<ol>
<li><a href="/">Home</a></li>
<li><a href="/notes/">Notes</a></li>
<li><a href="/notes/css-grid/" aria-current="page">CSS Grid</a></li>
</ol>
</nav>
<main>
<article>
<h1>Understanding CSS Grid</h1>
…
</article>
<aside aria-label="Related notes">
…
</aside>
</main>
<footer>
<p>© 2026 Stacktrick</p>
<nav aria-label="Footer">…</nav>
</footer>The landmarks list now reads: banner; navigation, Primary; search; navigation, Breadcrumb; main; complementary, Related notes; content info; navigation, Footer. A user can press D (or pick “main” from the rotor) and land on the article immediately. The page looks identical, because elements like <header> and <main> are plain display: block boxes with no default styling beyond that.
A few decisions in there worth explaining.
Each <nav> has a label. With three navigation landmarks, a list showing “navigation, navigation, navigation” is useless. aria-label="Primary", “Breadcrumb” and “Footer” make them distinct. Don’t include the word “navigation” in the label; the role is already announced, so “Primary navigation navigation” is what you’d get.
Not every group of links is a <nav>. A <nav> is for major navigation blocks. A list of tags under an article or three social links in a footer usually doesn’t need its own landmark; too many landmarks become noise in the same way too few are useless.
The <aside> is inside <main>. Placing related content inside <main> versus beside it is a judgement call. Inside means it’s part of this page’s content, which suits “Related notes”. Site-wide sidebars (an ad slot, a site-wide promo) usually sit outside <main>. Labelling the aside keeps it a complementary landmark either way and gives it a useful name.
<search> wraps the form. The <search> element is newer (supported in all current major browsers since 2023); <form role="search"> does the same thing for older browsers, and either is fine.
One main, and a skip link
There should be one visible <main> per page (you can have others if all but one are hidden, which single-page apps sometimes do). It should contain the page’s primary content, and it’s the target users jump to most.
Keyboard users who don’t use a screen reader can’t jump by landmarks; browsers have no built-in key for it. A skip link fills that gap:
<body>
<a class="skip-link" href="#main">Skip to main content</a>
<header>…</header>
<main id="main" tabindex="-1">…</main>
</body>.skip-link {
position: absolute;
left: -9999px;
}
.skip-link:focus {
left: 1rem;
top: 1rem;
z-index: 100;
background: #fff;
padding: 0.5rem 1rem;
}It’s hidden off-screen until it receives focus. The first Tab on the page reveals “Skip to main content” in the top-left; Enter moves focus to <main>. The tabindex="-1" on <main> makes it focusable by script and link target without adding it to the Tab order, which helps some browsers move keyboard focus (not just scroll) to it.
When to use ARIA roles directly
The ARIA landmark roles (role="banner", role="navigation", role="main" and so on) do the same job on any element. With modern HTML you rarely need them: the first rule of ARIA is to use the native element when one exists. Writing <nav role="navigation"> is redundant, and audit tools may flag it. The roles remain useful when you can’t change the element, such as a third-party widget’s markup, or role="search" on a <form> for wider support.
A <section> without a name is not a landmark, and that’s intentional: most sections are just thematic groupings. If a section is important enough to jump to, name it with aria-labelledby pointing at its heading, which turns it into a region landmark:
<section aria-labelledby="pricing-title">
<h2 id="pricing-title">Pricing</h2>
…
</section>Interview practice
A single-page app renders everything inside <div id="app">. The audit says “page has no main landmark” and “content is not contained by landmarks”. The app has a top bar with a logo and user menu, a left-hand navigation list, and a content area that changes per route. What do you change, and is there anything route-specific to consider?
Answer
Inside #app, change the top bar to <header>, the left navigation to <nav aria-label="Main menu"> (or similar), and the content area to <main>. Put the user menu inside the header (it isn’t major site navigation, so it doesn’t need its own <nav>), and wrap any footer in <footer>. The header and footer are only banner and contentinfo if they aren’t nested inside <main>, <article>, <aside>, <nav> or <section>; a <div id="app"> wrapper doesn’t affect that, so the mapping still works. Add a skip link to #main as the first focusable element.
For routes, make sure each route renders a single <main> that persists rather than nesting a new one per view, and that each view begins with an <h1>. On route change, move focus to the new <h1> or to <main> and update document.title, because a client-side route change doesn’t announce anything the way a page load does. Then verify the landmark list with a screen reader or a browser extension and check that nothing important sits outside a landmark.