// guide

Headings & Landmarks: Page Structure

Screen reader users almost never read a page top to bottom — they jump between headings and landmarks the way sighted users skim with their eyes, using them as a live table of contents to find what they came for. Getting this structure right is the single highest-leverage accessibility decision you make on any page, and it makes content more scannable for everyone.

beginner html wcag-1.3.1

// 01 · how screen reader users navigate

Sighted users scan a page. Their eyes jump to bold text, large headings, and visually grouped regions. They almost never read every word in order. Screen reader users do the exact same thing — but instead of using their eyes, they use keyboard shortcuts that jump between structural elements.

The two primary navigation modes are headings and landmarks:

  • Heading navigation — In NVDA and JAWS, the H key jumps to the next heading. 1 through 6 jump to the next heading at that specific level. In VoiceOver, the rotor (Ctrl+Option+U) lists every heading on the page.
  • Landmark navigation — In NVDA, D jumps between landmarks. In JAWS, R. In VoiceOver, the rotor includes a Landmarks list. Users can jump straight to "main", "navigation", or "search" without tabbing through every link.

The 2024 WebAIM screen reader survey found that over 70% of screen reader users navigate by heading first when they reach a new page. Landmarks come second. Reading the page sequentially is rare, and reserved for content where structure can't help — like a long article they're committed to reading in full.

The takeaway: your headings and landmarks are the table of contents. If they're missing or wrong, the page is unnavigable in the same way a paper book without chapter titles is unreadable in any order.

The mental model Imagine reading a page where every heading has been turned into invisible text. That's what your page looks like to a screen reader user when your structure is broken. Good structure isn't a bonus — it's the only way the page is navigable.

// 02 · the heading hierarchy rules

The Heading Hierarchy Rules

Heading levels (<h1> through <h6>) describe the structure of your content, not its visual size. Four rules cover almost every case:

  1. Exactly one <h1> per page. The h1 is the page's primary subject — usually the same as the page title or a close variant. Multiple h1s confuse the document outline and make heading navigation noisy.
  2. Never skip levels descending. An h2 can be followed by another h2 or an h3, but never an h4. Skipping a level signals to assistive tech that a section is missing.
  3. Siblings share a level. Sections of equal importance use the same heading level. If "Pricing" is an h2, "Features" should also be an h2, not an h3.
  4. Nest by topic, not by visual indent. A subsection's heading is one level deeper than its parent's. The hierarchy reflects what the content is, not how the page looks.

Good hierarchy

<h1>Accessible Forms</h1>
  <h2>Labels</h2>
    <h3>Visible Labels</h3>
    <h3>Hidden Labels</h3>
  <h2>Validation</h2>
    <h3>Inline Errors</h3>
    <h3>Submit Errors</h3>
  <h2>Required Fields</h2>

The outline reads like a coherent table of contents. Every level descends one step at a time. Siblings share a level. A screen reader user pressing 2 repeatedly jumps through the three top-level topics in order.

Broken hierarchy

<h1>Accessible Forms</h1>
  <h4>Labels</h4>          <!-- Skipped h2 and h3 -->
    <h2>Visible Labels</h2>  <!-- Jumped back up -->
  <h3>Validation</h3>        <!-- Sibling of "Labels" but at a different level -->

Each line breaks one of the rules. The outline is unreadable. A screen reader user pressing 2 would jump into a subsection, miss the top-level topics, and have no way to know how the content is organized.

It is fine to "go back up" After a deeply nested section, you can return to a higher level — that's how you start the next sibling section. h3 followed by h2 is perfectly correct. The rule is only about descending: never skip a level on the way down.

// 03 · choosing heading text

Choosing Heading Text

A heading is a label. Read out of context, it should still describe the content beneath it. Screen reader users hear headings in lists — without prose between them — so vague headings are useless.

Front-load the keyword

The most informative word should come first. Screen reader users skimming a heading list often only hear the first few words before deciding whether to continue.

  • Bad: "Introduction to how screen readers navigate by heading"
  • Good: "How screen readers navigate by heading"

Avoid generic openers

"Welcome", "Introduction", "Overview", and "Hello" tell the user nothing. They're noise in a heading list.

  • Bad: "Welcome"
  • Good: "How screen readers navigate by heading"

Don't repeat the page title

The h1 already says "Accessible Forms". The first h2 should not be "Accessible Forms" or "About Accessible Forms". Move into the actual content.

Describe what's beneath, not above

The heading labels the section that follows it. "What we just covered" is wrong; "Common pitfalls" is right.

The "navigated to" test Imagine a screen reader user just jumped to this heading from the heading list. With zero context, do they know what this section is about? If yes, the heading is doing its job. If they need to read the surrounding paragraphs to understand, rewrite it.

// 04 · visual vs semantic hierarchy

Visual vs Semantic Hierarchy

Designers often want a "small bold thing" at the top of a paragraph — a label, a category, a tagline. That's not a heading. A heading is a structural anchor that screen readers list in the table of contents. If something is decorative or a label, use the right element for the job.

Use <p><strong> or <b> for emphasis

<!-- Wrong: not a section heading, just bold text -->
<h6>Available now</h6>
<p>Our newest plan launches today.</p>

<!-- Right: emphasis without polluting the heading outline -->
<p><strong>Available now.</strong> Our newest plan launches today.</p>

Don't fake a heading with a styled <div>

If the text genuinely is a section heading, use a real heading element. Screen readers don't see your CSS — they see HTML.

<!-- Wrong: looks like a heading, isn't one -->
<div class="heading-style">Pricing</div>

<!-- Right: real heading, can be styled however you want -->
<h2 class="heading-style">Pricing</h2>

The semantic level must match the visual hierarchy

An h3 should never look bigger than the h2 above it. If your design has a "small section header" that visually comes before a "large section header", the small one is the parent — they're at the wrong levels. Fix the markup, not the CSS.

Conversely, it's fine to use a CSS class to size a heading down or up within the correct level. Visual size doesn't have to match level for level — just the relative hierarchy. <h2 class="h3-size"> is acceptable; <div class="h2"> is not.

The rule of thumb Use heading elements for sections. Use <p><strong> for emphasis. Use CSS classes for visual styling. Never substitute one for another.

// 05 · the html5 landmark elements

The HTML5 Landmark Elements

HTML5 introduced semantic elements that map directly to ARIA landmark roles. Use them. They give screen reader users a built-in navigation menu of your page's regions.

Element Landmark role When it counts
<header> banner Only at the top level of the page. Inside <article>, <section>, or <main>, it's not a landmark — just a structural grouping.
<nav> navigation Always a landmark. Use for site nav, page nav, breadcrumbs, table of contents, footer link groups.
<main> main Always a landmark. Only one per page. Wraps the unique content of this page (not the chrome).
<aside> complementary Always a landmark. Sidebars, related-content panels, pull quotes, ads.
<footer> contentinfo Only at the top level of the page. Inside <article> or <section>, it's not a landmark.
<section> region Only counts as a landmark if it has an accessible name (aria-label or aria-labelledby). Without a name, it's just a generic block.
<article> Not a landmark. Used for self-contained content (a blog post, a news item) but doesn't appear in the landmark list.
<form> form Only counts as a landmark when given an accessible name. Most forms don't need to be landmarks — leave them unnamed.
<search> search Newer element (2023). Always a landmark. Wraps a site search form. Fall back to <div role="search"> if you need older browser support.

The pattern: reach for the native HTML element first. <nav> is better than <div role="navigation">. Native elements come with the role for free, and they survive across frameworks, CSS resets, and DOM changes that strip ARIA attributes.

// 06 · aria landmark roles

ARIA Landmark Roles

Every HTML5 landmark element has an equivalent ARIA role. Use the role only when the native element isn't available — for example, in legacy code, framework-generated markup that strips semantic elements, or DOM constraints that force a <div>.

ARIA role Native equivalent Notes
role="banner" <header> at top level Only one banner per page.
role="navigation" <nav> Multiple allowed. Label them if there's more than one.
role="main" <main> Only one main per page.
role="complementary" <aside> Multiple allowed. Label if more than one.
role="contentinfo" <footer> at top level Only one contentinfo per page.
role="search" <search> Use the role if you need broader browser support than <search> currently has.
role="form" <form> with name Requires an accessible name to register as a landmark.
role="region" <section> with name Requires a label. Without aria-label or aria-labelledby, the role does nothing.
Native HTML always wins Reach for <nav>, <main>, <aside> first. Use the role only when something prevents the native element. Doubling up (<nav role="navigation">) is harmless but redundant — drop the role.

// 07 · labeling multiple landmarks

Labeling Multiple Landmarks

Most pages have more than one navigation region — site nav at the top, footer links at the bottom, maybe a breadcrumb in between. To a screen reader, those are three "navigation" landmarks. Without labels, the user hears "navigation, navigation, navigation" and has no way to tell them apart.

Solve it by labeling each one with aria-label:

<nav aria-label="Main">
  <ul>...</ul>
</nav>

<nav aria-label="Breadcrumb">
  <ol>...</ol>
</nav>

<nav aria-label="Footer">
  <ul>...</ul>
</nav>

The same applies to multiple <aside> elements, multiple labeled <section> regions, or multiple <form> landmarks. Whenever there are two or more of the same landmark type, label every one of them.

Don't include the role in the label

Screen readers already announce the role. Adding it to the label makes the announcement repeat itself.

  • Bad: <nav aria-label="Main navigation"> — announces as "Main navigation, navigation"
  • Good: <nav aria-label="Main"> — announces as "Main, navigation"
  • Bad: <section aria-label="About region">
  • Good: <section aria-label="About">

Use aria-labelledby when a visible heading exists

If a section already has a visible heading, point to it instead of duplicating the text:

<section aria-labelledby="pricing-heading">
  <h2 id="pricing-heading">Pricing</h2>
  ...
</section>

This keeps the visible label and the accessible label in sync. If the heading text changes, the landmark name updates automatically.

// 09 · common structure mistakes

Common Structure Mistakes

Multiple <main> elements Only one <main> per page. The HTML spec is explicit. If you have two, screen readers either pick one arbitrarily or announce both, neither of which is what you want. Wrap the unique page content in a single <main>; everything else (nav, footer, sidebars) goes outside it.
Skipping heading levels <h2> followed by <h4> breaks the hierarchy. If you want a smaller-looking heading, use CSS to size it down — keep the level correct. Visual size and semantic level are independent: <h3 class="small"> is fine; <h4> in place of an <h3> is not.
Wrapping the whole page in <article> <article> is for self-contained content — a blog post, a news item, a forum reply. It's not a layout wrapper. Putting your page header, nav, and footer inside <article> tells screen readers the entire page chrome is part of one syndicatable piece of content. Use <main> for primary content; reserve <article> for actual articles.
Generic <section> without a label <section> only becomes a region landmark if it has an accessible name. <section>Sidebar content</section> is just a styled div — no landmark, no benefit. Either add aria-labelledby pointing to a heading, or use a more specific element like <aside>.
Use <main> even with a skip link Skip links work better when the target is a real landmark. <div id="main-content"> is a valid skip target, but <main id="main-content"> is announced as "main, region" when focus lands there, confirming to screen reader users that the jump succeeded.

// 10 · anatomy of a well-structured page

Anatomy of a Well-Structured Page

Here's a typical page with every structural element in its right place. Comments explain why each is there.

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Pricing — Acme</title>
</head>
<body>

  <!-- Skip link: first focusable element so keyboard users
       can bypass the nav -->
  <a href="#main-content" class="skip-link">Skip to main content</a>

  <!-- Top-level <header> = banner landmark.
       Holds branding and primary nav. -->
  <header>
    <a href="/" aria-label="Acme home">Acme</a>

    <!-- Labeled because there's another <nav> in the footer -->
    <nav aria-label="Main">
      <ul>
        <li><a href="/features/">Features</a></li>
        <li><a href="/pricing/">Pricing</a></li>
      </ul>
    </nav>
  </header>

  <!-- <main> = main landmark. Skip link target.
       Only one per page. -->
  <main id="main-content">

    <!-- Breadcrumb is a nav, so label it -->
    <nav aria-label="Breadcrumb">
      <ol>
        <li><a href="/">Home</a></li>
        <li><span aria-current="page">Pricing</span></li>
      </ol>
    </nav>

    <!-- Exactly one <h1> -->
    <h1>Pricing plans</h1>

    <!-- h2 children of the h1 -->
    <section aria-labelledby="plans-heading">
      <h2 id="plans-heading">Available plans</h2>

      <h3>Starter</h3>
      <p>...</p>

      <h3>Pro</h3>
      <p>...</p>
    </section>

    <section aria-labelledby="faq-heading">
      <h2 id="faq-heading">FAQ</h2>
      <p>...</p>
    </section>

  </main>

  <!-- <aside> = complementary landmark.
       Related content, not primary. -->
  <aside aria-labelledby="related-heading">
    <h2 id="related-heading">Related</h2>
    <ul>...</ul>
  </aside>

  <!-- Top-level <footer> = contentinfo landmark.
       Only one per page. -->
  <footer>
    <nav aria-label="Footer">
      <ul>
        <li><a href="/privacy/">Privacy</a></li>
        <li><a href="/terms/">Terms</a></li>
      </ul>
    </nav>
    <p>© 2026 Acme</p>
  </footer>

</body>
</html>

A screen reader user opening this page sees: skip link first, then a landmark menu of "banner, main, complementary, contentinfo" plus three navigations labeled "Main", "Breadcrumb", "Footer". The heading list reads: "Pricing plans, Available plans, Starter, Pro, FAQ, Related". Both lists describe the page perfectly.

// 11 · testing page structure

Testing Page Structure

You can verify your structure without a screen reader. Run through these in order:

  1. View the heading outline. Install a browser extension like HeadingsMap, Accessibility Insights, or axe DevTools. The outline panel should read like a coherent table of contents — every level descends one step at a time, no skips, no extras.
  2. List the landmarks. Use a landmark navigator (NVDA D key, JAWS R key, VoiceOver rotor → Landmarks). Every major region of the page should be reachable. There should be exactly one banner, one main, and one contentinfo. Multiple navigations and complementaries should each have distinct labels.
  3. Disable CSS. In Firefox: View → Page Style → No Style. In Chrome: use the Web Developer extension or DevTools. The page should still be readable in source order. If content collapses into nonsense, the DOM order doesn't match the visual order, and screen reader users are reading the wrong thing.
  4. Tab from the top. Open the page, press Tab once. The skip link should be the first focusable element, even if it's invisible until focused. Press Tab again — focus should land on the first nav link.
  5. Run an automated audit. Lighthouse, axe DevTools, and WAVE all flag missing landmarks, multiple <main> elements, and skipped heading levels. Automation can't catch bad headings (the text is opaque to it), but it catches the structural mistakes reliably.

For deeper testing methodology with screen readers, see the Screen Reader Testing guide and the Keyboard Testing guide.

// 12 · wcag success criteria

WCAG Success Criteria

Criterion Level How structure satisfies it
1.3.1 Info and Relationships A Structural relationships between content (sections, headings, regions) must be programmatically determinable. Real heading elements and HTML5 landmarks satisfy this; visual styling alone does not.
2.4.1 Bypass Blocks A Users must be able to skip repeated content (like nav). Skip links and landmarks are the two standard techniques. A page with a <main> landmark and a working skip link satisfies this.
2.4.6 Headings and Labels AA Headings (and form labels) must describe the topic or purpose of the content they introduce. "Welcome" fails; "How to install" passes.
2.4.10 Section Headings AAA Where content is organized into sections, those sections must have headings. Long-form pages should not have unlabeled blocks of content.
1.3.6 Identify Purpose AAA The purpose of regions, links, and form fields can be programmatically determined. Landmark roles identify the purpose of each region (main, navigation, complementary, etc.).