empty-heading
Headings must not be empty
Checks that heading elements (<h1>-<h6> or role="heading") have a non-empty accessible name.
Applies to. Applies to elements with a heading role: native <h1>-<h6>, or any element whose role attribute resolves to heading (the first token naming a known role, matched case-insensitively, so role="foo heading" counts). A native heading keeps its role when no token is a known role, and loses it to any other resolved role. A native <hx> marked role="none" or "presentation" is still a heading when it can take focus or carries a global ARIA attribute, as WAI-ARIA's presentational roles conflict resolution has browsers ignore that role then. A custom element whose ElementInternals give it the heading role (this.attachInternals().role), with no role attribute naming another, counts as a native element with that role does.
Expectation. The heading has a non-empty accessible name: aria-label, aria-labelledby, visible text content not hidden from assistive technology, or (as a last resort) a title attribute. An empty heading is announced as "heading, level N" with nothing else, which is confusing when navigating by heading. Reported at FAIL although no WCAG criterion is claimed: an empty heading is itself the barrier, as the ACT rule ffd0e9 treats it. A heading whose name may come from content no script can read (a closed shadow root, content nested too deep, a custom element naming itself through ElementInternals) is asked about instead, as the *-name-present rules do.
Examples
<h2>Section title</h2>
The heading has visible text content.
<h2></h2>
The heading has no accessible name at all — nothing for a screen reader to announce.
How to check
When this rule can’t decide, it reports cantTell and leaves the question to a person. Each question it can ask is listed here under its reason code, with the steps to answer it. A finding carries the same steps as howToCheck, in the scan’s language. See Output schema.
name_closedContent
- Find the heading the report names and note the text or icon it shows on screen.
- Open the Accessibility pane in the browser's developer tools and read the element's accessible name, which the browser works out from content no script can read.
- With a screen reader, move to the element and listen to the name announced.
Pass. The accessibility tree shows a name for the heading, taken from its content, and the screen reader announces it.
Fail. The heading has no name in the accessibility tree, since its content gives none, which goes against the best practice that headings have content (ACT ffd0e9).
name_contentTooDeep
- Find the heading the report names and note the text it shows on screen.
- Open the Accessibility pane in the browser's developer tools and read the element's accessible name, which the browser works out from all of its nested content.
- With a screen reader, move to the element and listen to the name announced.
Pass. The accessibility tree shows a name for the heading, taken from its nested content, and the screen reader announces it.
Fail. The heading has no name in the accessibility tree, since its content gives none, which goes against the best practice that headings have content (ACT ffd0e9).
name_internals
- Inspect the heading the report names, a custom element, and note its role.
- Open the Accessibility pane in the browser's developer tools and read the element's accessible name.
- With a screen reader, move to the element and listen to the name announced.
Pass. The accessibility tree shows a name for the heading, set through its internals or otherwise, and the screen reader announces it.
Fail. The heading has no name in the accessibility tree and the screen reader announces only its role, which goes against the best practice that headings have content (ACT ffd0e9).