HTML Landmarks for accessibility

Why we need accessibility Landmarks ?

It helps the physical impairment users who are using a screen reader and allows them to jump to a particular section of a webpage.

Primary Landmark Elements

  • <header> - defines your header
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <link rel="stylesheet" href="./main.css" />
    </head>
    <body>
        <header>Define your complete header including navigation etc.</header>
    </body>
</html>
  • <nav> - defines your navigation
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <link rel="stylesheet" href="./main.css" />
    </head>
    <body>
        <header>
            <nav>
                <ul>
                    <li></li>
                    <li></li>
                    <li></li>
                    <li></li>
                </ul>
            </nav>
        </header>
    </body>
</html>
  • <footer> - defines your footer in which we can add the copyrights
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <link rel="stylesheet" href="./main.css" />
    </head>
    <body>
        <footer>Your footer content goes here...</footer>
    </body>
</html>
  • <main> - it is the main container in which we can place our content
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <link rel="stylesheet" href="./main.css" />
    </head>
    <body>
        <main>
            <div>
                <h1>Heading</h1>
                <p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. At, dolores.</p>
            </div>
        </main>
    </body>
</html>
  • <aside>
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <link rel="stylesheet" href="./main.css" />
    </head>
    <body>
        <aside>
            <h1>This is heading text in aside Tag</h1>
            <p>This is paragraph text in aside Tag</p>
        </aside>
    </body>
</html>
  • <section> - it is the container in which we can place our content and make them separate
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Document</title>
        <link rel="stylesheet" href="./main.css" />
    </head>
    <body>
        <header>
            <nav>Your navigation structure goes here...</nav>
        </header>
        <main>
            <section>
                <div>
                    <h1>Heading</h1>
                    <p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. At, dolores.</p>
                </div>
            </section>
            <section>Second content here</section>
            <section>Third content here</section>
        </main>
        <footer>Your footer content goes here...</footer>
    </body>
</html>

Primary Landmark Roles :

ElementsImplicit ARIA Role
<header>role="banner"
<nav>role="navigation"
<main>role="main"
<aside>role="complementary"
<footer>role="contentinfo"
<section>role="region" (when labeled)
<form>role="form" (when labeled)

Note : <header> maps to banner and <footer> maps to contentinfo only when they are direct children of <body>. When nested inside <article>, <section>, or other sectioning elements, they do not carry those implicit roles.


Understanding it with the example :

As showing in the image below is the example of current page without using a landmark

Missing landmark

Now let’s fix the issue showing in the axeDevtools - Document should have one main landmark. To fix this issue we have added one role="main" to the container of the page where our main content lies and notice that now we are not getting the error.

Fixed missing landmark


Resources :


Scenarios and Edge Cases

Multiple identical landmarks on a page

When a page contains more than one instance of the same landmark (e.g., two <nav> elements), screen readers list them identically, making it impossible for users to distinguish between them.

Solution : Use aria-label or aria-labelledby to give each landmark a unique name :

<nav aria-label="Main navigation">
    <ul>
        <li><a href="/">Home</a></li>
        <li><a href="/about">About</a></li>
    </ul>
</nav>

<nav aria-label="Footer navigation">
    <ul>
        <li><a href="/privacy">Privacy</a></li>
        <li><a href="/terms">Terms</a></li>
    </ul>
</nav>

Landmarks in Single Page Applications (SPAs)

  • In SPAs, the page does not fully reload on navigation. Screen readers may not announce the new content automatically.

  • Best practices :

  • Move focus to the new page’s <h1> or <main> element after a route change.

  • Use an aria-live="polite" region to announce the page title change.

  • Ensure the <title> element is updated on each route change.

// After route change
document.title = 'New Page Title - My App';
const mainHeading = document.querySelector('h1');
if (mainHeading) {
    mainHeading.setAttribute('tabindex', '-1');
    mainHeading.focus();
}

Hidden and off-canvas landmarks

  • Off-canvas navigation (hamburger menus) must use aria-hidden="true" when closed so screen readers skip them.

  • When the menu opens, remove aria-hidden and manage focus appropriately.

  • Content behind an open modal or overlay should have aria-hidden="true" or inert applied to prevent screen reader access.

<!-- Closed state -->
<nav id="mobile-nav" aria-label="Mobile navigation" aria-hidden="true">
    <!-- nav items -->
</nav>

<!-- Open state (via JavaScript) -->
<nav id="mobile-nav" aria-label="Mobile navigation" aria-hidden="false">
    <!-- nav items -->
</nav>

Nested landmarks

  • Landmarks should not be unnecessarily nested. For example, placing a <nav> inside an <aside> inside a <section> creates a deeply nested structure that screen readers must enumerate verbosely.

  • Keep the landmark tree as flat and meaningful as possible.

Using <section> as a landmark

  • <section> only becomes a landmark when it has an accessible name via aria-label or aria-labelledby. Without a label, it is just a generic grouping element.
<!-- This IS a landmark -->
<section aria-labelledby="faq-heading">
    <h2 id="faq-heading">Frequently Asked Questions</h2>
</section>

<!-- This is NOT a landmark -->
<section>
    <h2>Frequently Asked Questions</h2>
</section>

If you found this helpful, share it with someone who's building for the web.

- Vinay Ranjan