HTMLHeadings (<h1>–<h6>)

Headings (<h1>–<h6>)

HTML gives you six levels of section heading: <h1> through <h6>. Browsers render <h1> the largest and <h6> the smallest by default, but that visual size is only a side effect. The real job of a heading is to describe the section that follows it, so both sighted readers skimming the page and assistive technology building a document outline can understand its structure at a glance.

The Six Levels

Each level nests conceptually inside the one above it. An <h1> introduces the whole page (or a major top-level section), an <h2> introduces a subsection of that, an <h3> a subsection of the <h2>, and so on down to <h6>.

heading-levels.html

HTML
<h1>Web Development Guide</h1>

<h2>Front-End Basics</h2>
<h3>HTML</h3>
<h3>CSS</h3>
<h3>JavaScript</h3>

<h2>Back-End Basics</h2>
<h3>Databases</h3>
<h3>APIs</h3>
Web Development Guide
  Front-End Basics
    HTML
    CSS
    JavaScript
  Back-End Basics
    Databases
    APIs
One <h1> Per Page (the Convention)

The HTML specification technically allows multiple <h1> elements on a page, especially when sections are nested inside <section> or <article>. In practice, most style guides, SEO tools, and accessibility checklists still recommend exactly one <h1> per page — it should describe what the entire page is about, similar to a book's title. Everything else builds down from there.

one-h1.html

HTML
<h1>Understanding CSS Grid</h1>

<h2>What Is a Grid Container?</h2>
<p>...</p>

<h2>Defining Rows and Columns</h2>
<p>...</p>

<h2>Placing Items on the Grid</h2>
<p>...</p>
Why the convention persists
Browsers, screen readers, and search engines all build a document outline from headings. A single, clear <h1> gives that outline an unambiguous root. Multiple competing <h1> elements on one page make the outline harder to interpret even though the spec permits it.
The HTML5 Outline Algorithm (and Why It's Rarely Used)

Early HTML5 drafts proposed an "outline algorithm" where each <section>, <article>, <aside>, and <nav> created its own implicit outline, letting an <h1> inside a nested <section> behave like an <h2> relative to the page. In theory this meant you could reuse <h1> freely inside self-contained components.

No browser or screen reader implements it
The outline algorithm was removed from the HTML Living Standard because no major browser or assistive technology ever shipped it. Screen readers build their heading navigation from the literal heading levels in the markup (h1h6), not from sectioning depth. Write headings for the outline that actually exists today, not the one that was proposed and abandoned.
Don't Skip Levels

Jumping from <h2> straight to <h4> (skipping <h3>) leaves a gap in the outline. Screen reader users often navigate heading-by-heading with a single keystroke; a skipped level makes them wonder if they missed content or if the page is just poorly structured.

Markup

Problem

Fix

<h1><h3>

Skips h2 — outline has an unexplained gap

Insert an <h2> or demote the h3 to h2

<h2><h2><h5>

Sudden three-level jump for a minor subsection

Use <h3> for the subsection instead

Choosing headings by font size

Picks <h3> because it "looks right" visually

Choose the level by document structure, then style with CSS

Headings Are Structure, Not a Font-Size Tool

It's tempting to reach for <h3> just because you want bold, medium-sized text somewhere on the page. Resist it. Headings are read aloud differently by screen readers, listed in "jump to heading" navigation menus, and weighted by search engines as signals of page structure. If you just want bold or large text with no structural meaning, style a <p> or <span> with CSS instead.

misuse-vs-correct.html

HTML
<!-- Misuse: heading picked for visual size only -->
<h4>Free Shipping on Orders Over $50</h4>

<!-- Correct: no structural meaning needed, so style a paragraph -->
<p class="banner-text">Free Shipping on Orders Over $50</p>
Let a browser extension check your outline
Browser extensions like "HeadingsMap" render the heading outline of any page you visit. Running one against your own pages is a fast way to catch skipped levels or duplicate <h1>s before publishing.
Headings and Accessibility
  • Screen readers expose a "headings list" (e.g. NVDA's Insert+F7) so users can scan a page's structure without reading every word.

  • Pressing H (or 1–6 for a specific level) in many screen readers jumps directly between headings — skipped levels break that mental model.

  • A logical heading structure benefits everyone: it doubles as an at-a-glance table of contents for sighted skimmers too.

Headings Inside Sectioning Elements

It's common — and encouraged — to restart at <h2> inside each <section> or <article> on a page, rather than continuing to increment forever. Think of each section as a chapter: its own internal heading, followed by subheadings that make sense for that chapter's content.

sections-with-headings.html

HTML
<h1>Company Blog</h1>

<article>
  <h2>Why We Rebuilt Our Design System</h2>
  <h3>The Old System's Limitations</h3>
  <h3>Our New Token-Based Approach</h3>
</article>

<article>
  <h2>Announcing Our Q3 Roadmap</h2>
  <h3>New Features</h3>
  <h3>Performance Improvements</h3>
</article>
Each article restarts its own subheading structure
Both articles above reuse <h2> and <h3> — that's fine, because each <article> describes its own self-contained story. The page still only has one <h1> describing the page as a whole.
Headings vs Visual Hierarchy in Design

Designers often hand off a mockup where "section title" text is visually smaller than "page title" text, but where the actual reading order and structural nesting doesn't match a strict h1 → h2 → h3 chain. When that happens, resist matching the heading level to the mockup's font size — pick the heading level that matches the content's actual role in the outline, then use CSS to make it look however the design calls for.

structure-first.html

HTML
<!-- The design shows this "featured" heading larger than the h1 -->
<h1 class="page-title">Getting Started</h1>
<h2 class="featured-heading">Quick Setup (5 minutes)</h2>

structure-first.css

CSS
.page-title {
  font-size: 1.5rem;
}

/* Visually larger than the h1, but still structurally an h2 */
.featured-heading {
  font-size: 2.25rem;
  font-weight: 800;
}
Headings for Page Titles vs the <title> Element

Don't confuse the visible <h1> with the <title> element in <head>. They often contain similar text, but they serve different audiences: <title> shows in the browser tab, bookmarks, and search results; <h1> is the first visible heading inside <body>. Keep both, and keep them consistent enough that a user isn't confused when the tab title doesn't match what they see on the page.

Element

Where it appears

Purpose

<title>

Browser tab, bookmarks, search results

Identifies the document itself, outside the rendered page

<h1>

Top of the rendered page content

Introduces what the visible content is about

Testing Your Heading Structure
  • Open your browser dev tools and list every h1–h6 on the page in document order — does it read like a sensible table of contents?

  • Check for exactly one <h1> that describes the whole page.

  • Scan for skipped levels (h2 straight to h4) and fix them by inserting the missing level or adjusting the jump.

  • Confirm no heading was chosen purely because of its default font size rather than its structural role.

Automated tools catch most heading issues
Tools like axe DevTools, Lighthouse, and WAVE all flag missing <h1>s, skipped heading levels, and multiple <h1>s automatically — run one against your page before shipping.
Styling Headings Without Changing Their Level

Sometimes a design calls for a heading to look smaller or larger than its browser default without changing its structural meaning. CSS handles that cleanly — never "cheat" by choosing a different heading level purely to get a certain font size, and never fake a heading with a bold, oversized <span> when the text really is a heading structurally.

heading-styles.css

CSS
h1 {
  font-size: 2.5rem;
  line-height: 1.2;
}

h2 {
  font-size: 1.875rem;
  margin-top: 2.5rem;
}

h3 {
  font-size: 1.375rem;
  color: #444;
}
A bold <span> is not a heading
Wrapping text in <span class="heading"> and styling it to look like a heading gives sighted users the right visual impression but gives screen reader users nothing — it won't appear in their headings list or jump navigation at all. If it functions as a heading, mark it up as one.
Headings in Navigation and Widget Components

Reusable components like cards, modals, and sidebars often include their own internal heading (a card title, a modal title). These should usually be a lower-level heading (h2/h3) that fits logically into the page's overall outline at the point where the component is rendered — not always the same fixed level regardless of context.

component-heading-context.html

HTML
<main>
  <h1>Dashboard</h1>

  <section aria-label="Recent activity">
    <h2>Recent Activity</h2>

    <!-- Each card's title fits under the section's h2 -->
    <article class="card">
      <h3>New comment on your post</h3>
    </article>
    <article class="card">
      <h3>Your invoice was paid</h3>
    </article>
  </section>
</main>
Component libraries can't always guess the right level
A generic <Card title="..."> component often can't know whether it will be rendered directly under an <h1> or nested three sections deep. Many component APIs expose a headingLevel or as prop for exactly this reason — pass it explicitly based on where the component is actually used.