Heading Structure Accessibility: Fix H1s, Skips and Landmarks

What a missing h1 or a skipped heading level really costs, which findings WCAG requires you to fix, and the markup that fixes them.

By Denis Omerovic ·

A scan says a page is missing an h1, or that it skips from h2 to h4, and the obvious questions follow. Does it matter? Is it a WCAG failure or a style preference? And how do you fix it without redesigning the page? This guide answers all three, with the markup, the criteria behind each finding, and the page builder habits that cause most of them.

How screen reader users actually navigate a page

Sighted visitors skim a page by its big bold text. Screen reader users skim it by its heading markup. Every major screen reader can list the headings on a page or jump from one to the next with a single key, and for most users that is the first thing they reach for.

In WebAIM's Screen Reader User Survey #10 (1,539 responses, December 2023 to January 2024), 71.6% of respondents said navigating through the headings is how they first try to find information on a long page. 88.8% found heading levels very or somewhat useful (57.0% very useful, 31.8% somewhat useful).

That is the real cost of a broken outline. If the section titles are styled paragraphs, a screen reader user gets no headings to jump between and has to listen to the page from the top. If the levels are scrambled, the list they pull up no longer says which section belongs inside which. This is the sentence for a manager or client: headings are the table of contents for people who cannot see the page.

What WCAG requires, and what is best practice

It helps to separate the hard requirements from the advice, because a scan reports both and they deserve different urgency.

Finding Where it comes from How firm
Text that looks like a heading but is not marked up as one 1.3.1 Info and Relationships, failure F2 A WCAG failure (Level A)
A heading that does not describe its section, or an empty heading 2.4.6 Headings and Labels (AA), 1.3.1 A WCAG failure
No way to skip past repeated navigation 2.4.1 Bypass Blocks (A) A WCAG failure, met by headings, landmarks or a skip link
Skipped heading levels Technique G141 and the W3C headings tutorial Strong advice, not a pass or fail on its own
Missing h1, or more than one W3C headings tutorial Best practice

Two things in that table surprise people. First, 2.4.6 does not require headings at all: its Understanding document says plainly that the criterion only asks that headings, where they exist, describe their topic. Second, WCAG has no rule that says "never skip a level". G141 says headings should be properly nested, and the W3C tutorial says skipping ranks "can be confusing and should be avoided where possible". So a skipped level is worth fixing, but it is not the same severity as a fake heading. That is why AccessGuard reports a skipped level as a warning and a missing h1 as a notice, while an empty heading is an error.

One h1, and what it should say

The h1 is the title of the page's own content. On an article, that is the article title. On a product page, the product name. On a service page, the service.

The most common mistake is putting the site logo in the h1 on every page, which gives every page the same main heading. The W3C tutorial shows the content title as rank 1 for exactly this reason.

HTML
<!-- Broken: every page has the same h1, and the real title is an h2 -->
<header>
  <h1><a href="/"><img src="logo.svg" alt="Northwind Studio"></a></h1>
</header>
<main>
  <h2>Kitchen renovation in Leeds</h2>
</main>

<!-- Fixed: the logo is a plain link, the page title is the h1 -->
<header>
  <a href="/"><img src="logo.svg" alt="Northwind Studio home"></a>
</header>
<main>
  <h1>Kitchen renovation in Leeds</h1>
</main>

HTML allows more than one h1, and WCAG does not forbid it, but one h1 per page is the convention screen reader users expect: press the key for level 1 and you land on the title. Keep it to one unless you have a reason.

Skipped levels: why they happen in page builders

Hand-written templates rarely skip levels. Page builders do it all the time, for one reason: the heading block offers a visual size and a heading level, and people pick the level for its size.

A typical service page built in a page builder looks like this:

HTML
<!-- Broken: levels chosen for font size -->
<h1>Bathroom fitting</h1>
<h4>Trusted by local homeowners</h4>   <!-- small tagline, picked h4 for size -->
<h2>What we do</h2>
<h3>Full refits</h3>
<h3>Wet rooms</h3>
<h5>Get a quote</h5>                   <!-- footer call to action -->

<!-- Fixed: levels follow the outline, the tagline is not a heading -->
<h1>Bathroom fitting</h1>
<p class="tagline">Trusted by local homeowners</p>
<h2>What we do</h2>
<h3>Full refits</h3>
<h3>Wet rooms</h3>
<h2>Get a quote</h2>

The other common source is a reused section or widget. A "latest posts" block that was built with h3 titles for the blog page gets dropped on the home page straight under the h1. The fix is to choose the level where the block is placed. In the WordPress block editor, the Heading block has a level picker separate from the font size controls. Most page builders have a similar "HTML tag" or "level" setting on their heading widget, so check that setting, not the size, when you audit.

Two smaller rules from the W3C tutorial keep the outline sensible:

  • A tagline, a kicker above a title, or a "Read more" label is not a heading. Use a paragraph.
  • Headings in fixed parts of the page, such as a sidebar or footer, should keep the same level on every page rather than changing with the content.

Headings are not styling

The fix for both problems above is the same: pick the level from the outline, then style it however the design wants. CSS does not care what the tag is.

HTML
<!-- Broken: looks like a heading, is a paragraph (fails 1.3.1, see F2) -->
<p class="big-bold">Our process</p>

<!-- Fixed: a real h2, styled small because the design wants it small -->
<h2 class="heading-small">Our process</h2>
CSS
/* Size is a class, not a level */
.heading-small {
  font-size: 1.125rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}

Separating size from level in the design system is the change that stops this from coming back. If the type scale has "display", "title" and "label" classes, an editor can make an h2 look like anything without reaching for an h5.

The opposite mistake matters too. Bold text inside a paragraph that is not a section title should not become a heading just because it is bold. Only real section titles go in the outline.

Landmarks that make the outline usable

Headings say what each section is about. Landmarks say which part of the page you are in: the site header, the navigation, the main content, the footer. Screen readers can jump between landmarks the same way they jump between headings, and they help meet 2.4.1 Bypass Blocks by letting users skip the repeated navigation.

Fewer people use landmarks than headings. In the same WebAIM survey, 3.7% said landmarks are their first way to find information on a long page, but 17.9% use them whenever they are available, 13.9% often and 31.5% sometimes. They cost almost nothing, so add them.

Native HTML elements give you landmarks without any ARIA. The WAI-ARIA Authoring Practices list how they map:

  • <header> becomes the banner landmark only when it is not inside article, aside, main, nav or section. The same rule applies to <footer> and the contentinfo landmark.
  • <nav>, <main>, <aside> and <search> always map to navigation, main, complementary and search.
  • <section> and <form> become landmarks only when they have an accessible name.
HTML
<!-- Broken: everything is a div, so there are no landmarks -->
<div class="top">...</div>
<div class="menu">...</div>
<div class="content"><h1>Bathroom fitting</h1>...</div>
<div class="bottom">...</div>

<!-- Fixed: one of each, two navs told apart by their labels -->
<header>
  <a href="#main" class="skip-link">Skip to main content</a>
  <nav aria-label="Main">...</nav>
</header>
<main id="main">
  <h1>Bathroom fitting</h1>
  ...
</main>
<footer>
  <nav aria-label="Footer">...</nav>
</footer>

The Authoring Practices give three rules worth checking on every template: all visible content should sit inside a landmark, each page should have one main, and when a landmark type appears more than once, as with two nav elements, each needs its own label.

What a scan can and cannot tell you

Heading and landmark structure is largely machine readable, so automated checks are useful here. AccessGuard runs automated WCAG 2.2 AA checks in a real browser and reports a missing h1, skipped heading levels, empty headings, a missing skip link, a missing main landmark and duplicate main landmarks without labels, each with the WCAG criterion it relates to. You can see what it looks for on the heading structure checker page.

What a scan cannot judge is meaning. It cannot tell whether "Section 2" describes its content, whether a bold paragraph was meant to be a heading, or whether the outline matches how the page reads. Automated checks find many problems but cannot confirm conformance. For that, open the page with a screen reader and pull up its headings list, which takes a couple of minutes; our 20 minute screen reader pass shows how.

Heading structure checklist

  • Every page has one h1, and it is the title of that page's content, not the logo.
  • Every visual section title is a real heading element, not a styled paragraph or div.
  • Levels go down one at a time (h2 to h3, never h2 to h4), and back up as far as needed.
  • Taglines, kickers and "read more" labels are paragraphs, not headings.
  • Reusable blocks and widgets have their heading level set for where they are placed.
  • Font size comes from a class, so nobody picks a level for its size.
  • Every heading describes its section, and none are empty.
  • The page has header, nav, one main and footer, with repeated landmarks labelled.
  • A skip link or the landmarks let keyboard users get past the navigation.
  • The headings list in a screen reader reads like a sensible table of contents.

Want to see where a page stands? Run a free scan on it. For the rest of the audit, read the most common accessibility issues and how to fix them and the accessibility audit checklist. If the site runs on WordPress, the WordPress accessibility guide covers the theme and plugin side.

Check a client site for this

Audit any page against WCAG 2.2 AA for free and see every finding in about 30 seconds.

Run a free audit