Empty heading (empty-heading)

The empty-heading rule flags a heading that has no text a screen reader can announce – an empty <h2>, or one that holds only a hidden icon. Screen reader users hear "heading level 2" and nothing else. The fix is to give the heading real text, or to remove the heading element if it is only there for spacing.

What the rule means

Every heading – h1 to h6, or an element with role="heading" – needs an accessible name. axe accepts any of these:

  • text inside the heading, including text that is visually hidden but available to screen readers,
  • the alt text of an image inside the heading,
  • aria-label or aria-labelledby,
  • a title attribute, as a last resort.

A heading that contains only whitespace, a non-breaking space, an icon marked aria-hidden="true" or an image with alt="" fails. Headings hidden entirely – with display: none or the hidden attribute – are not checked, because nobody reaches them.

Note: this is a best practice, not a WCAG failure in itself. It relates to 1.3.1 Info and Relationships, because an empty heading claims a structure that is not there, and to 2.4.6 Headings and Labels, because a heading should describe what follows.

Who is affected

Mainly blind and partially sighted people using a screen reader. They move through a page by headings or open a list of all headings, and an empty one appears there as a blank entry or as "heading level 2" with no words. It costs a keystroke, suggests missing content and makes the rest of the outline harder to trust. Voice control users who say a heading's name to jump to it also have nothing to say.

Why the check fails

  • Spacing with headings: editors press Enter in a heading style and leave an empty <h2>&nbsp;</h2> behind in the rich-text editor.
  • Icon-only headings: a card title that is just an icon font or SVG with aria-hidden="true".
  • Empty template slots: a teaser or accordion component renders its <h3> even when the editor left the title field empty.
  • Headings filled by JavaScript that stay empty when the data does not load.
  • Logos in headings with alt="" because the image was treated as decorative.

How to fix it

Decide first whether it is a heading at all. If it is, give it text; if it only makes space, replace it with CSS.

<!-- Before: an empty h2 used as a spacer, and a heading with only an icon -->
<h2 class="section-spacer"></h2>
<div class="card">
  <h3 class="card__title"><span class="icon icon-star" aria-hidden="true"></span></h3>
  <p>Our most popular products this month.</p>
</div>
<!-- After: spacing is a div with CSS; the heading has real text -->
<div class="section-spacer"></div>
<div class="card">
  <h3 class="card__title"><span class="icon icon-star" aria-hidden="true"></span> Customer favourites</h3>
  <p>Our most popular products this month.</p>
</div>

For components, do not render the heading element when its title field is empty. In a CMS, clean up empty headings in the editor's source view, and check whether the editor's paste handling creates them.

How to test it manually

  1. Open the heading list: the free HeadingsMap extension, the WAVE extension's structure view, or a screen reader (NVDA: Insert + F7; VoiceOver: VO + U).
  2. Look for blank entries or entries that only say "heading".
  3. For each heading, read the announced text on its own. Does it say what the section is about?
  4. The rule only checks that some text exists. A heading reading "More" or "Section 1" passes, and so does one whose visible text differs from its aria-label – a person has to judge both.

None directly – empty-heading is a best practice. It relates to 1.3.1 Info and Relationships and 2.4.6 Headings and Labels. Whether headings describe their content (2.4.6) is a judgement no automated check makes; in Reviseberg that criterion stays "untested" until a person records a manual result.

How Reviseberg reports it

Reviseberg runs empty-heading on every crawled page. Each empty heading is one occurrence, so an empty slot in a shared component shows up on every page that uses it. The issue list shows the rule as a best practice with its severity, the number of elements and pages, and the points a fix gets you back; it never counts towards a WCAG criterion or appears in a compliance report. The detail view shows the selector and HTML snippet of each heading and every affected page. You can mark a finding as "ignore" or "false positive" with a reason; every decision is logged.

  • undefined – a heading that skips a level.
  • undefined – the page has no level-one heading.
  • undefined – the same problem for links: no text to announce.

Find empty headings across your site – get a free scan

Frequently asked questions

Is an empty heading a WCAG failure?

No. axe-core lists empty-heading as a best practice, and Reviseberg reports it that way. It still confuses screen reader users and supports WCAG 1.3.1 and 2.4.6.

Does visually hidden text count?

Yes. Text hidden with a screen-reader-only class is still announced, and the rule accepts it. Text hidden with display: none or aria-hidden="true" does not count.

Is an image inside a heading enough?

Yes, if the image has meaningful alt text – that text becomes the heading's name. With alt="" the heading is empty.

Why not just add aria-label to the empty heading?

It passes the rule, but sighted users still see an empty space or an icon. Visible text serves everyone at once.

Sources

  1. Deque University, axe-core 4.13 rule empty-heading – https://dequeuniversity.com/rules/axe/4.13/empty-heading
  2. W3C, ACT rule: Heading has non-empty accessible name – https://www.w3.org/WAI/standards-guidelines/act/rules/ffd0e9/
  3. W3C WAI, Page Structure Tutorial: Headings – https://www.w3.org/WAI/tutorials/page-structure/headings/
  4. W3C, Understanding SC 2.4.6 Headings and Labels – https://www.w3.org/WAI/WCAG22/Understanding/headings-and-labels

See what a scan finds on your site

One page in about thirty seconds, no email needed. The full report covers up to 100 pages and a keyboard journey.