---
title: "Fix heading order for better accessibility"
description: "Fix heading order issues in Lighthouse accessibility audits. Ordered headings convey semantic structure for easier navigation."
canonical_url: "https://unlighthouse.dev/learn-lighthouse/accessibility/heading-order"
last_updated: "2025-01-18"
---

Screen reader users rely on headings to navigate your page. Skipped levels break navigation and make content structure unclear.

## What's happening

Headings (`<h1>` through `<h6>`) must form a logical hierarchy, increasing by only one level at a time. Skipping from `<h2>` to `<h4>` breaks the user's mental model of the page. Users cannot tell if the skipped `<h3>` represents missing content or poor markup.

Ordered headings let users jump between sections, understand the page structure, and orient themselves.

## Diagnose

### Chrome DevTools

1. Open DevTools (F12)
2. Press Ctrl+Shift+P (Cmd+Shift+P on Mac)
3. Type "Show headings" and select the option
4. Review the heading outline - look for skipped levels

Quick console check:

```js
const headings = [...document.querySelectorAll('h1, h2, h3, h4, h5, h6')]
let lastLevel = 0

headings.forEach((h) => {
  const level = Number.parseInt(h.tagName[1])
  if (level > lastLevel + 1 && lastLevel !== 0) {
    console.warn(`Skipped heading level: ${h.tagName} after H${lastLevel}`, h)
  }
  lastLevel = level
})
```

### Accessibility tree

1. DevTools > Elements > Accessibility pane
2. Expand the tree and look at heading structure
3. Levels must descend sequentially: 1 > 2 > 3, not 1 > 3.

## Fix

### 1. Restructure heading levels

Fix the hierarchy by adjusting heading levels:

```html
<!-- Before: Skipped level -->
<h1>Product Catalog</h1>
<h3>Electronics</h3>  <!-- Wrong: skipped h2 -->
<h3>Clothing</h3>

<!-- After: Sequential levels -->
<h1>Product Catalog</h1>
<h2>Electronics</h2>  <!-- Correct: h2 follows h1 -->
<h2>Clothing</h2>
```

For nested sections, continue the hierarchy:

```html
<h1>Product Catalog</h1>
  <h2>Electronics</h2>
    <h3>Phones</h3>
    <h3>Laptops</h3>
  <h2>Clothing</h2>
    <h3>Shirts</h3>
    <h3>Pants</h3>
```

### 2. Style independently from semantics

Use CSS to achieve desired visual sizes without breaking semantics:

```html
<!-- Keep correct heading level, style differently -->
<h2 class="text-3xl font-bold">Main Section</h2>
<h3 class="text-2xl font-semibold">Subsection</h3>
<h4 class="text-xl">Nested Item</h4>
```

```css
/* Override default heading styles */
.section-title {
  font-size: 1.5rem;
  font-weight: 600;
}

h2.section-title,
h3.section-title,
h4.section-title {
  /* Same visual appearance, correct semantic level */
}
```

### 3. Fix component headings

Components often hardcode heading levels. Make them configurable:

```html
<!-- Bad: Component always uses h3 -->
<div class="card">
  <h3>Card Title</h3>
</div>

<!-- Good: Heading level is contextual -->
<div class="card">
  <h2>Card Title</h2>  <!-- or h3, h4 depending on context -->
</div>
```

Pass the heading level as a prop.

## Framework examples

<callout icon="i-logos-nuxt-icon">

**Nuxt / Vue**

Make heading levels dynamic in components:

```html
<script setup>
defineProps({
  level: {
    type: Number,
    default: 2,
    validator: v => v >= 1 && v <= 6
  }
})
</script>

<template>
  <component :is="`h${level}`" class="card-title">
    <slot />
  </component>
</template>
```

Usage:

```html
<Card>
  <CardHeading :level="2">Products</CardHeading>
  <Card>
    <CardHeading :level="3">Electronics</CardHeading>
  </Card>
</Card>
```

</callout>

<callout icon="i-logos-react">

**React**

Dynamic heading component:

```jsx
function Heading({ level = 2, children, className }) {
  const Tag = `h${level}`
  return <Tag className={className}>{children}</Tag>
}

// Usage with context for automatic levels
function Section({ children }) {
  const parentLevel = useHeadingLevel()
  return (
    <HeadingLevelProvider value={parentLevel + 1}>
      {children}
    </HeadingLevelProvider>
  )
}
```

</callout>

## SEO impact: passage ranking

Correct heading structure is critical for **Google Passage Ranking**.

Google indexes and ranks individual passages from a page independently. It relies on `<h>` tags to understand where one topic ends and another begins.

- **Broken hierarchy**: Confuses the crawler about the relationship between sections.
- **Logical hierarchy**: Allows Google to snip a specific section and serve it as a **Featured Snippet**.

Fixing heading order maximizes your content's surface area in search results.

## Verify the fix

1. **Re-run Lighthouse** - The "Heading elements appear in a sequentially-descending order" audit must pass.
2. **Check heading outline** - In DevTools, run:

```js
Array.from(document.querySelectorAll('h1,h2,h3,h4,h5,h6'), h => `${h.tagName}: ${h.textContent.trim().slice(0, 50)}`)
  .join('\n')
```

The output must show logical nesting without skips.

1. **Screen reader test** - Use heading navigation (H key in NVDA/JAWS) to move through headings. The structure must be logical.

## Common mistakes

- **Using headings for visual styling**: Use a `<p>` or `<span>` with CSS if you want big bold text that isn't a section heading. Reserve headings for actual document structure.
- **Multiple h1 elements**: Use only one `<h1>` per page. Multiple `<h1>` elements confuse screen readers and search engines.
- **Starting at h2 or h3**: The first heading on a page must be `<h1>`. Starting at a lower level breaks the hierarchy.
- **Using divs with heading roles**: `<div role="heading" aria-level="2">` is fragile. Use real heading elements.
- **Hiding headings for visual design**: Use visually-hidden CSS if a heading shouldn't be visible. `display: none` hides it from screen readers.

## Related issues

Heading order issues often appear alongside:

- [Document Title](/learn-lighthouse/accessibility/document-title) - The title and h1 should describe the same topic
- [Bypass](/learn-lighthouse/accessibility/bypass) - Proper headings enable skip navigation
- [HTML Lang](/learn-lighthouse/accessibility/html-has-lang) - Both affect how screen readers interpret structure

## Test your entire site

Heading structure issues often creep in through reusable components, templates, or CMS content. A card component using `<h3>` might be correct on one page but break hierarchy on another. Unlighthouse scans every page and flags heading order violations across your entire site.
