# Missing or misplaced landmarks, and content outside them

Source: https://easeweb.dev/learn/landmarks
Topics: Page structure > Landmarks mark the main areas, and all content sits inside one (WCAG 1.3.1)
The fix: Mark up each area of the page as a landmark, once and at the top level: the site header and anything above it in a header, menus in nav, the content in one main and the footer in a footer, side by side rather than inside a main. Either use those elements, or, where the template's elements can't change, add role="banner", "navigation", "main" and "contentinfo" to the existing divs.
Test with: screen reader
References: https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html https://www.w3.org/WAI/WCAG22/Techniques/html/H101 https://www.w3.org/WAI/WCAG22/Techniques/aria/ARIA11 https://www.w3.org/WAI/ARIA/apg/practices/landmark-regions/ https://html.spec.whatwg.org/multipage/grouping-content.html#the-main-element https://www.w3.org/TR/html-aam-1.0/ https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/header https://dequeuniversity.com/rules/axe/4.10/region https://dequeuniversity.com/rules/axe/4.10/landmark-one-main

This is a recreated teaching example, not a finding about a named client. The code is a starting pattern: test it in your own product. No completed assistive-technology test is claimed here.

## What happens

A bookshop's pages share one layout. A thin strip at the top says "Free delivery on orders over $30". Below it are the logo, Harbour Books, and a menu of four links, then the page's own content, here a list of new releases, and a footer with Contact and Returns. A screen reader user who wants the new releases does what they do on any shop: open the landmark list, which screen readers offer so people can jump to an area of the page, or press the key that moves from landmark to landmark. On these pages the list is empty and the key finds nothing, so unless they jump by heading, they read from the top, through the strip, the logo and every menu link.

A redesign then moves the pages into a new app layout, and the list now reads "main", "navigation", "main". The first main is almost the whole page, so choosing it lands on the logo. The second is the content. Banner and content information, the header and footer, are missing from the list, and the delivery strip is in no landmark at all, so moving landmark by landmark passes it by.

## Why it fails

The page shows four areas, and 1.3.1 needs structure that people see to be available in code too. HTML has an element for each of these areas: a `header` at the top level of the page is the banner landmark, `nav` is a navigation landmark, `main` is the main content, and a `footer` at the top level is the content information landmark. WCAG techniques H101 and ARIA11 use them to meet 1.3.1. The first version uses a `div` for all four, so the code exposes none of them.

The second version has landmarks, but in the wrong places:

- The layout wraps everything in a `main`, so the main landmark starts at the logo and claims the header and footer as content.
- A `header` or `footer` inside `main`, `article`, `aside`, `nav` or `section` is not a banner or content information landmark, so the site's header and footer stop being landmarks at all.
- The page adds its own `main` inside the layout's. HTML allows one `main` that isn't hidden, with no ancestors other than `html`, `body`, `div`, a form with no accessible name or autonomous custom elements, but both elements are still exposed as main, so a screen reader lists two.
- The delivery strip sits above the outer `main`, in no landmark.

WCAG lists landmarks as sufficient techniques, not requirements, and no WCAG failure technique names a page without them, so an auditor may pass a page whose headings carry the same structure. Here nothing else does: the menu and the footer have no headings, and the only heading is the content's own.

## Who is affected

Both versions look exactly right, so nobody who reads the page by sight notices. The failure is in the markup, and it repeats on every page that shares the layout.

## What automation and AI miss

axe has a rule for most of this: `region` for content outside every landmark, `landmark-one-main` for a page with no main, `landmark-no-duplicate-main` and `landmark-main-is-top-level` for the second, nested main, and four more for a banner or content information landmark used twice or placed inside another landmark. axe classes them all as best practice, so audits that run only the WCAG rules never report them. None of them sees the header and footer inside `main`: a header there is meant to be an ordinary header, as an article's is, so axe leaves it alone. This audit sends that case to a person: a page with no banner, or no content information, whose first header or footer sits inside `main` or a sectioning element is raised for review. Quick fixes can add a landmark rather than move one. They put `role="main"` on yet another wrapper, `role="banner"` on the header that is still inside `main`, where it becomes a banner inside the main content, or a label on every `section`, which fills the landmark list with regions nobody wants. Whether each area sits in the right landmark, and whether a nested header is the site's or an article's, is something a person has to judge against the page.

## Before: a page built from divs

```html
<div class="top-bar">Free delivery on orders over $30</div>
<div class="header">
  <a href="/" class="logo">Harbour Books</a>
  <div class="menu">
    <a href="/fiction">Fiction</a>
    <a href="/non-fiction">Non-fiction</a>
    <a href="/children">Children</a>
    <a href="/events">Events</a>
  </div>
</div>
<div id="content">
  <h1>New releases</h1>
  <ul class="books">…</ul>
</div>
<div class="footer">
  <a href="/contact">Contact</a>
  <a href="/returns">Returns</a>
</div>
```

Every area is a `div`, so the page has no landmarks: the landmark list is empty, and everything on the page sits outside one.

## Before: everything inside the layout's main

```html
<div class="top-bar">Free delivery on orders over $30</div>
<main class="layout">
  <header>
    <a href="/" class="logo">Harbour Books</a>
    <nav>
      <a href="/fiction">Fiction</a>
      <a href="/non-fiction">Non-fiction</a>
      <a href="/children">Children</a>
      <a href="/events">Events</a>
    </nav>
  </header>
  <main id="content">
    <h1>New releases</h1>
    <ul class="books">…</ul>
  </main>
  <footer>
    <a href="/contact">Contact</a>
    <a href="/returns">Returns</a>
  </footer>
</main>
```

The outer `main` turns the header and footer into plain content, the inner `main` is a second one inside the first, and the strip is outside both, so the landmark list reads "main", "navigation", "main".

## After: header, nav, main and footer elements

```html
<div class="layout">
  <header>
    <div class="top-bar">Free delivery on orders over $30</div>
    <a href="/" class="logo">Harbour Books</a>
    <nav>
      <a href="/fiction">Fiction</a>
      <a href="/non-fiction">Non-fiction</a>
      <a href="/children">Children</a>
      <a href="/events">Events</a>
    </nav>
  </header>
  <main id="content">
    <h1>New releases</h1>
    <ul class="books">…</ul>
  </main>
  <footer>
    <a href="/contact">Contact</a>
    <a href="/returns">Returns</a>
  </footer>
</div>
```

The header, the content and the footer sit side by side in a `div`, which is allowed around `main`, and there is one `main`. The strip moves inside the header, where it is part of the banner. The landmark list now reads "banner", "navigation", "main", "content information", in the order the page shows them, and nothing sits outside a landmark.

## After: landmark roles on the existing divs

```html
<div class="layout">
  <div class="header" role="banner">
    <div class="top-bar">Free delivery on orders over $30</div>
    <a href="/" class="logo">Harbour Books</a>
    <div class="menu" role="navigation">
      <a href="/fiction">Fiction</a>
      <a href="/non-fiction">Non-fiction</a>
      <a href="/children">Children</a>
      <a href="/events">Events</a>
    </div>
  </div>
  <div id="content" role="main">
    <h1>New releases</h1>
    <ul class="books">…</ul>
  </div>
  <div class="footer" role="contentinfo">
    <a href="/contact">Contact</a>
    <a href="/returns">Returns</a>
  </div>
</div>
```

The divs keep their elements and classes and gain the landmark roles, which is ARIA11, so the landmark list reads exactly as with the elements. The roles hold only where the divs are: a `role="banner"` or `role="main"` inside another landmark is a landmark in the wrong place again, so the three areas still have to sit side by side, and the strip still moves into the header.

## Which option to use

Both give screen readers the same four landmarks and meet 1.3.1. Use the elements wherever you can change the template: they do the same job with no attributes to keep in step, a `header` or `footer` that later ends up inside `main` stops being a landmark instead of becoming one in the wrong place, and scripts and tools that read the HTML rather than the accessibility tree see them too. Keep the roles for a template whose elements you can't change, such as a theme whose styles and scripts select `div.header`, and switch to the elements when you can.

## Implementation decisions

Let one layout own the page's shell. The root layout renders the header, one `main` and the footer side by side, and each page renders its content inside that `main`, in a `div` or `section` of its own, never another `main`. In a single-page app, keep the `main` in place on a route change and replace what is inside it. A `main` with the `hidden` attribute doesn't count, so an app that keeps several views in the page can give each one a `main` as long as only one is shown at a time.

Find a landmark for everything the layout adds around the content. Each kind belongs somewhere:

- A strip or promotion above the site header goes inside the `header`, as part of the banner.
- A cookie notice or a newsletter bar appended after the footer becomes a `section` with a name (`<section aria-label="Cookie notice">`), which makes it a region, or a dialog if it is one.
- A third-party chat or feedback button usually injects its own markup at the end of `body`. Check where it lands, and, if it is outside every landmark, ask the vendor or wrap the container you load it into.

A skip link can stay before the header, outside every landmark: that is where people expect it, and axe allows it there.

An article's own `header` and `footer` are right inside it. A product card, a blog post or a review can have a header with its title and a footer with its date; inside `article` or `main` they are not banners, and they shouldn't be. Don't give them `role="banner"` or `role="contentinfo"`.

Name landmarks only when they need it. One banner, one main and one content information landmark need no name. When a role appears more than once, such as two `nav` elements, each needs its own name, as [navigation landmarks that all sound the same](/learn/landmark-names) explains. A `section` becomes a region landmark only when it has a name, so name the few that people would want to jump to and let headings divide the rest.

Current browsers map these elements to their roles, so `<main role="main">` and `<nav role="navigation">` repeat what the element already says. Leave the extra role out.

## Verify the fix

1. In the browser's accessibility tree, or with a landmarks extension, list the page's landmarks: one banner, the navigation, one main and one content information, none of the three inside another landmark, and every piece of visible content inside one of them.
2. Open a screen reader's landmark list (in NVDA, Insert + F7 and choose Landmarks; in VoiceOver, the rotor). Check that each area of the page is there once, in the order the page shows them, then choose main and check that it lands on the content's first heading.
3. Move through the page landmark by landmark (D in NVDA, R in JAWS) and read from the end of each landmark to the start of the next: nothing should come between them, including the strips and notices around the header and footer.
4. Repeat on each template (home, listing, product, checkout) and with any cookie notice or chat widget showing, since layouts and injected content differ between them.

Record the OS, browser and assistive-technology versions, the build, the date, and the actual result of each step. Repeat whenever the layout or a third-party widget changes.

## Limits

This is a starting pattern, not a guarantee for every application. Paths, class names and the `…` in the lists are placeholders. Screen readers word the landmark list differently ("content info" or "content information", with "landmark" or "region" after it, or neither), so listen for the areas rather than the exact phrasing. Landmarks give screen reader users a way past the header, and WCAG 2.4.1 accepts them as one, but sighted keyboard users have no landmark keys, so for them the page should still have [a skip link past repeated navigation](/learn/skip-link) for them. This guide covers whether the page has the right landmarks in the right places. It doesn't cover naming repeated landmarks, for which see [navigation landmarks that all sound the same](/learn/landmark-names), or the headings inside them, for which see [headings chosen by size, not by structure](/learn/heading-structure). Test the complete journey with your target assistive technology.

---

Get this guide in your coding agent: MCP https://easeweb.dev/mcp (setup at https://easeweb.dev/services/developers)
