auditmysitev2.0.2

Empty heading

headings/empty

WCAG success criterion
2.4.6 (level AA)
Outcome class
Automatic. An automated check that can report a confirmed violation.
Area
Accessibility, Structure & Semantics
Default severity
medium
axe-core rule
headings/empty

Why we flag this

Screen reader users find an entry without content in the list of headings.

<h1>–<h6> or role="heading" without text and without aria-label.

What the rule checks

Scope. Rendered elements with the heading role: h1-h6 and elements with role=heading, also inside open shadow roots.

Expectation. Fails for every rendered heading whose accessible name is empty: no text, and no name from aria-label, aria-labelledby or an image's non-empty alt. A heading whose only content is a decorative image (alt="") is empty. role="heading" elements are headings like h1-h6 (ACT ffd0e9 applies to the semantic role). Headings hidden with display:none are not presented and do not apply. One finding per empty heading, at the heading.

Source. WCAG 2.2 SC 1.3.1 Info and Relationships, SC 2.4.6 Headings and Labels; ACT ffd0e9 (Heading has non-empty accessible name)

Detection: shared; needs dom, computed_style.

Failing example

The check reports a violation here. Corpus case structure_head_defects, element h2#empty-decorative. The page may contain further findings for other rules.

structure_head_defects.htmlHTML
<!DOCTYPE html>
<html lang="en" xml:lang="de">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>
    </title>
    <style>
        body { font: 16px/1.5 sans-serif; margin: 0 16px; }
        a { display: inline-block; min-width: 44px; min-height: 44px; line-height: 44px; }
        input, button { min-width: 44px; min-height: 44px; }
        .is-hidden { display: none; }
    </style>
</head>
<body>
    <!-- A CMS template with broken head data and empty heading shells (#783):
         lang and xml:lang name different languages, the title holds only
         whitespace, and several headings carry no accessible name. -->
    <a href="#main" class="skip-link">Skip to content</a>
    <header>
        <p>Acme Hardware</p>
    </header>
    <nav aria-label="Main">
        <ul>
            <li><a href="/">Home</a></li>
            <li><a href="/shop/">Shop</a></li>
        </ul>
    </nav>
    <main id="main">
        <h1>Product catalogue</h1>
        <!-- Empty: no text, and the only image is decorative. -->
        <h2 id="empty-decorative"><img src="divider.png" alt=""></h2>
        <p>Hand tools for every workshop.</p>
        <!-- Named by an image's alternative text: not empty. -->
        <h2 id="named-by-alt"><img src="logo-tools.png" alt="Tools by Acme"></h2>
        <p>Hammers, saws and planes.</p>
        <!-- Named by aria-labelledby: not empty. -->
        <h2 id="named-by-ref" aria-labelledby="garden-label"></h2>
        <p id="garden-label">Garden</p>
        <!-- An ARIA heading with no name is an empty heading as well. -->
        <div id="empty-aria-heading" role="heading" aria-level="2"></div>
        <p>Spades and rakes.</p>
        <!-- Hidden and empty: not rendered, not in the outline. -->
        <h2 id="hidden-empty" class="is-hidden"></h2>
        <h2>Contact</h2>
        <p>Call us during opening hours.</p>
    </main>
    <footer>
        <p>Acme Hardware, Berlin</p>
    </footer>
</body>
</html>

Passing example

The check passes here and reports nothing. Corpus case structure_outline_clean. The page may contain further findings for other rules.

structure_outline_clean.htmlHTML
<!DOCTYPE html>
<html lang="en" xml:lang="en-GB">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Opening hours and contact – Acme Hardware Berlin</title>
    <style>
        body { font: 16px/1.5 sans-serif; margin: 0 16px; }
        a { display: inline-block; min-width: 44px; min-height: 44px; line-height: 44px; }
        input, button { min-width: 44px; min-height: 44px; }
    </style>
</head>
<body>
    <!-- Conforming look-alike for the structure rules (#783): every
         structure the rules look at is present and correct. -->
    <a href="#main" class="skip-link">Skip to content</a>
    <header>
        <p>Acme Hardware</p>
    </header>
    <nav aria-label="Main">
        <ul>
            <li><a href="/">Home</a></li>
            <li><a href="/shop/">Shop</a></li>
            <li><a href="/contact/">Contact</a></li>
        </ul>
    </nav>
    <main id="main">
        <h1>Opening hours and contact</h1>
        <p>Our store in Berlin is open six days a week. We are happy to help you
           find the right tool for your project, and you can always call us before
           you visit to check that an item is in stock.</p>
        <h2>Opening hours</h2>
        <dl>
            <div><dt>Monday to Friday</dt><dd>8:00 to 18:00</dd></div>
            <div><dt>Saturday</dt><dd>9:00 to 14:00</dd></div>
        </dl>
        <h2>Directions</h2>
        <!-- English text with German proper names: names are exempt from
             3.1.2, and only two German function words occur. -->
        <p id="directions">The store is a short walk from the Haus der Kunst gallery and the
           old bridge that locals call die Brücke. Take the tram to the main square, then
           follow the river for about five minutes until you see our green sign.</p>
        <article>
            <h3>Store news</h3>
            <p>We now stock garden tools from local makers.</p>
        </article>
        <section aria-labelledby="contact-heading">
            <h2 id="contact-heading">Contact</h2>
            <p>Call us or send a message. Prices include <abbr title="value-added tax">VAT</abbr>.</p>
            <form action="/contact" method="post">
                <label for="contact-email">E-mail</label>
                <input id="contact-email" name="email" type="email" autocomplete="email" aria-describedby="contact-hint">
                <p id="contact-hint">We reply within one working day.</p>
                <button type="submit">Send</button>
            </form>
        </section>
    </main>
    <footer>
        <p>Acme Hardware, Berlin</p>
    </footer>
    <!-- Hidden leftover outside every landmark: not rendered, not content. -->
    <div hidden>Summer sale banner</div>
</body>
</html>

This page is generated from the rule catalogue and the rule contracts; see therule overview for how the outcome classes are derived.