accessibility

ARIA landmarks

MetricSpot checks for landmark regions (main, nav, header, footer, aside) so screen-reader users can skip directly to the content instead of every link in the menu.

What this check does

Parses the rendered HTML for landmark regions: the sectioning elements <main>, <nav>, <header>, <footer>, <aside>, <form>, <section> (with an accessible name), and their ARIA equivalents (role="main", role="navigation", etc.). The check fails if there’s no <main> (or role="main") on the page.

Why it matters

Screen readers expose a landmarks menu — JAWS uses R, NVDA uses D, VoiceOver uses the rotor. A user lands on your page and presses one key to jump to the main content, skipping the header and the global nav.

Without landmarks, that shortcut doesn’t exist. The user has to arrow through every link in your header, every promo banner, every login dropdown, every language switcher before they reach the article they came for. On a typical marketing site that’s 30 to 60 elements of audio before the first useful sentence.

Landmarks are also how WCAG 2.1 success criterion 1.3.1 (Info and Relationships) is satisfied for page structure, and how the EU Accessibility Act audits expect navigable regions to be exposed programmatically.

How to fix it

The minimum: wrap your primary content in <main>. Add <nav> around the primary navigation, <header> for the site header, <footer> for the site footer.

HTML:

<body>
  <header>
    <nav aria-label="Primary">...</nav>
  </header>
  <main>
    <h1>Page title</h1>
    <article>...</article>
    <aside aria-label="Related articles">...</aside>
  </main>
  <footer>...</footer>
</body>

Rules that trip people up:

  • Only one <main> per page. Multiple <main> elements break the landmark map.
  • Multiple <nav> regions need accessible names so a screen reader can tell them apart. Use aria-label="Primary", aria-label="Breadcrumb", aria-label="Footer" — don’t leave them unnamed.
  • <header> and <footer> only count as banner/contentinfo landmarks when they’re direct children of <body>. Inside an <article> they’re just sectioning content, not landmarks.
  • Don’t redundantly add role="main" to <main>. The native element already exposes the role.

Next.js (App Router):

// app/layout.tsx
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <header><SiteNav /></header>
        <main>{children}</main>
        <footer><SiteFooter /></footer>
      </body>
    </html>
  );
}

Astro:

---
// src/layouts/Base.astro
---
<html lang="en">
  <body>
    <header><Nav /></header>
    <main><slot /></main>
    <footer><Footer /></footer>
  </body>
</html>

WordPress: modern block themes wrap the post content in <main> automatically; classic themes need <?php wp_body_open(); ?> followed by your <main> wrapper in header.php and </main> before get_footer().

If you can’t change the markup (CMS lock-in, embedded widget), add role="main" to the existing wrapper div — same effect for assistive tech, less ideal for parsers.

Pair this with Declare page language and Form input labels — the three together cover most of the accessibility low-hanging fruit.

Frequently asked questions

No. When <header> and <footer> are direct children of <body>, browsers expose them as banner and contentinfo automatically. Adding the role is redundant and can confuse some older screen readers.

What about <section> — does it count as a landmark?

Only when it has an accessible name (aria-label or aria-labelledby). A bare <section> is just a generic region. If the section doesn’t need to be in the landmarks menu, use <div> instead.

Will adding landmarks affect SEO?

Indirectly. Google doesn’t rank on landmarks, but it does use the HTML5 outline (especially <main> and <article>) to identify the primary content versus chrome. Cleaner landmark structure correlates with cleaner extraction in AI Overviews and featured snippets.

Sources

Last updated 2026-05-11