sfw/fix
heading-order medium

Heading levels should only increase by one (skipped level)

Heading levels jump (an h2 followed by an h4), so the outline screen reader users navigate by has gaps and misleads them about structure.

What you see

Heading levels should only increase by one
Heading order invalid
Element: <h4>Refund window</h4>

What’s actually happening

Headings descend out of step — an h1, then straight to an h3, or an h2 followed by an h4 with no h3 between them. Sighted readers don't notice because the text is styled by size. But screen reader users navigate by jumping heading to heading (the H key in NVDA/JAWS, the rotor in VoiceOver), and the level numbers are how they build a mental outline. A skipped level reads as "a subsection is missing" or "this belongs to a parent that isn't there," so the page structure feels broken even though every section is present.

Common causes

  • A heading tag was chosen for its default font size rather than its position in the outline (someone wanted smaller text so they used <h4>)
  • A section was deleted or reordered and the surrounding levels never got renumbered, leaving a gap
  • Reusable components hardcode their own heading level (a card that always emits <h4>) and get dropped under a section where that level doesn't follow
  • Content pasted from another document (or a CMS rich-text field) brought its original, mismatched heading levels along
  • There's no h1 at all, or multiple sibling sections start at different levels, so the first jump is already wrong

How to fix it

  1. Map the outline and renumber sequentiallyList the headings in DOM order and assign levels by nesting depth: one h1 for the page, h2 for top sections, h3 for their subsections, and so on with no skips. Tools help here — run document.querySelectorAll('h1,h2,h3,h4,h5,h6') in the console, or use a heading-outline browser extension, to see the actual sequence and spot the jump.
  2. Style with CSS, never by changing the tagPick the heading level for meaning, then set its appearance in CSS. If an h3 needs to look small, give it a class and size it there — h3.compact{font-size:1rem}. Don't reach for an h4 to get smaller text; that's exactly what breaks the outline.
  3. Make component heading levels configurableFor reusable cards/panels, pass the heading level in as a prop instead of hardcoding it (e.g. an `as`/`level` prop that renders h2–h6). That way the same component slots in at the correct level wherever it's placed and doesn't force a jump.
  4. Start at h1 and keep one per pageEnsure the page has exactly one h1 naming its main topic, and that the first content heading below it is h2. A missing or duplicated h1 is often the root cause of the first skipped level the scanner reports.
  5. Verify by listening to the heading listOpen the screen reader's headings list (VoiceOver rotor, NVDA elements list) and read it top to bottom. It should sound like a sane table of contents — every level one step from its parent, no orphans.

Stop it recurring

Choose heading tags by document structure and handle all visual sizing in CSS, so levels never get picked for their default font size.

Related errors