2.4.10 Section Headings

WCAG 2.2 · 2.4.10 AAA Operable

What it requires

Where written content is organised into sections, those sections must be introduced by headings. The criterion is about presence, not styling: if a block of content is conceptually a distinct section, it needs a real heading to mark its start. It applies only when the content actually divides into sections — it does not force you to invent artificial divisions, and it does not dictate a specific heading level or wording. In HTML the headings should be true heading elements (<h2><h6>), so the section structure is exposed programmatically and not just visually.

Strong section headings help everyone, but they are essential for some users:

  • Screen-reader users navigate by heading (jumping H-to-H or opening a headings list); missing or fake headings leave them reading linearly with no map.
  • People with cognitive and learning disabilities rely on headings to chunk long content, preview structure, and find where they left off.
  • Low-vision and magnifier users, who see only a small slice of the page, use headings as orientation landmarks.

How to detect it

Checks for Section Headings
Check What to look for
Manual review Read the page and identify its sections. Does each distinct section begin with a heading? Watch for "headings" that are really bold or large text.
Screen reader Pull up the headings list (e.g. NVDA/JAWS) or rotor (VoiceOver). Every section should appear; the outline should match the visible structure.
Keyboard / structure Navigate by heading shortcut. If a visually obvious section is skipped, it lacks a real heading.
Zoom At 200–400% zoom, confirm the content still reads as headed sections rather than one undifferentiated wall of text.
Automated tools Largely a human judgement. axe and similar tools flag empty or skipped-level headings, but cannot tell whether a section that should have a heading is missing one.

How to fix it

  1. Identify the page's logical sections from the content itself.
  2. Give each section a real heading element, not styled text.
  3. Make the heading text describe the section's topic concisely.
  4. Keep levels nested logically (no skipping from h2 to h4).
  5. Pair each heading with its content using <section> where it clarifies the grouping.
<section>
  <h2>Shipping & returns</h2>
  <p>Orders ship within two business days...</p>
</section>

<section>
  <h2>Warranty</h2>
  <p>Every product is covered for 12 months...</p>
</section>

Copy-paste tests

Automated coverage

There is no fully automated axe-core rule for 2.4.10 — a tool cannot know which content blocks are conceptually distinct sections that require a heading. This criterion needs manual review, using the console check and steps below to surface candidates.

Run this in the browser console

section-headings-audit.js
// Read-only: lists landmark/section containers and the headings they hold.
const suspects = [...document.querySelectorAll('section, article, aside, [role="region"]')]
  .map(el => {
    const h = el.querySelector(':scope > h1, :scope > h2, :scope > h3, :scope > h4, :scope > h5, :scope > h6');
    if (!h) el.style.outline = '2px dashed orange'; // visual hint only
    return { el, heading: h ? h.textContent.trim() : '(none)', hasHeading: !!h };
  });
console.table(suspects.map(({ heading, hasHeading }) => ({ heading, hasHeading })));
console.log('Containers without a direct heading:', suspects.filter(s => !s.hasHeading).map(s => s.el));

What to check manually: for every visually distinct section, confirm a real heading element (not bold/large styled text) introduces it, and that the heading wording actually describes that section's topic — neither is something a script can confirm.