Quick answer: This audit fails when headings skip a level, such as an h1 followed directly by an h3. Fix it by choosing heading tags by document hierarchy rather than visual size, using exactly one h1, never skipping a level downward, and styling text size with CSS instead.
This Lighthouse audit fails when your page skips heading levels, for example, an <h1> followed by an <h3> with no <h2> between them. Screen readers expose the heading hierarchy as a document outline; skipped levels break that outline.
TL;DR
- What: Heading levels (h1 → h2 → h3 → h4...) must not skip a level downward.
- Why it matters: Screen reader navigation by heading is broken when levels jump.
- Fix: Use heading tags by semantic level, not by visual size. Style with CSS, not with which tag you pick.
What does the heading-order audit check?
Lighthouse walks the rendered DOM in source order and inspects every heading (<h1>–<h6>). It flags pages where a heading is more than one level lower than the previous one.
<h1>Page title</h1>
<h3>Subheading</h3> <!-- ❌ skipped h2 -->
Going back up is fine, <h3> followed by <h2> is OK. The audit only fails on downward jumps.
Why does heading order matter?
Screen readers expose page structure as a navigable outline. Users press H (or NVDA: 1, 2, 3 keys for specific levels) to jump between sections. When levels are skipped, the outline reads as: "Page title (level 1), Subheading (level 3, level 2 missing)." It implies there's a level 2 they missed, which is confusing.
It also hurts SEO subtly: Google's structured understanding of your page relies in part on the heading outline.
How do I audit page headings?
Auditing page headings means reading the page as a document outline instead of as a design. Three passes, cheapest first:
- Run Lighthouse or axe. The
heading-orderaudit flags skipped levels. axe DevTools adds rules Lighthouse does not report, notably "page must contain a level-one heading" and the empty-heading check. Both tools work per URL, so audit one URL per template rather than every page: headings come from templates, and so do heading bugs. - Read the outline, not the page. In DevTools, search the Elements panel for
h1, h2, h3, h4, h5, h6to list every heading in source order, or install a headings-map extension that draws the tree. The outline should read like a table of contents on its own. If it does not, the levels are wrong even when Lighthouse passes. - Check the four things automation misses. Exactly one
<h1>. No heading chosen for its size. No empty or icon-only heading. And heading text that still makes sense out of context, because screen reader users hear the heading list on its own, without the surrounding copy.
To print the outline of the page you are on, paste this into the console:
[...document.querySelectorAll('h1,h2,h3,h4,h5,h6')]
.forEach(h => console.log(' '.repeat(+h.tagName[1] - 1) + h.tagName + ' ' + h.textContent.trim()));
Indentation that jumps two levels at once is exactly what this audit reports. A heading that is visually large but sits deep in the indentation is the opposite bug: valid HTML, misleading design.
How do I fix heading order?
Use heading levels semantically, not visually. Pick the tag for what the content means, then size it with CSS.
<!-- BEFORE: tag chosen for visual size -->
<h1>Our pricing plans</h1>
<h3>Free tier</h3>
<h3>Pro tier</h3>
<!-- AFTER: semantic hierarchy -->
<h1>Our pricing plans</h1>
<h2>Free tier</h2>
<h2>Pro tier</h2>
If you want <h2> to look smaller than the original <h3>, do that in CSS:
h2 { font-size: 1.5rem; font-weight: 600; }
What are common heading-order scenarios?
Scenario: Card grids inside a section
<section>
<h2>Features</h2> <!-- correct level for the section -->
<article>
<h3>Real-time sync</h3> <!-- h3 because it's nested in h2's section -->
<p>...</p>
</article>
<article>
<h3>Offline mode</h3> <!-- also h3, sibling article -->
<p>...</p>
</article>
</section>
Scenario: Sidebar with its own headings
The page's main <main> outline doesn't include <aside> content. You can restart heading levels inside an <aside> if you want, but most accessibility experts recommend continuing the document hierarchy.
Scenario: Component libraries (e.g. design systems)
If your design system exports a <Card> component that hard-codes <h3> internally, you'll break heading order whenever you use it inside a section that doesn't have an h2. Solution: make the heading level a prop.
<Card heading="h2" title="Feature name" />
What heading-order pitfalls should I avoid?
- Don't have more than one
<h1>. HTML5 technically allows it via sectioning, but Lighthouse and screen readers expect one h1 per page. - Don't use
<h1>for branding/logo. That's what the<header>element + alt text is for. - Don't pick a heading tag because of its default size. Use CSS for sizing.
How do I verify heading order?
- Re-run Lighthouse. The
heading-orderaudit should pass. - View the document outline with a screen reader's heading list (VoiceOver: VO+U → Headings; NVDA: NVDA+F7).
- Use a browser extension like "Headings Map" or axe DevTools to visualize the heading tree.
Related audits
- Document does not have a main landmark, landmark structure
- Image alt attributes, screen reader content
- Meta description, SEO
Audit your URL at https://lighthouse-md.com.
Audit your page now
Paste your URL, get scores plus a CLAUDE.md plan for Claude Code.