We'd like to set Google Analytics cookies to learn how people use the Knowledge Base, so we can improve it. We set them only if you accept.

Skip to main content
Digital Accessibility Knowledge Base

missing-heading-semantics

Web validation

Headings must have the correct semantics so that users of assistive technologies can quickly navigate the page in a non-linear manner.

Sighted users recognize headings by their visual styling, such as a larger font or bold text, and use them to understand the page structure and scan for the section they need. Assistive technology users rely on heading semantics for the same purpose: screen readers list the headings on a page and let users jump between them. When text is styled to look like a heading but is marked up as a generic element, such as a <div>, <span>, or <p>, it is not exposed as a heading. Screen reader users miss the content hierarchy and cannot navigate to that section by heading.

To fix the issue, use one of these approaches, according to the heading's place in the page hierarchy:

  • Change the element to a native heading element, <h1> to <h6>. This is the preferred approach.
  • Add role="heading" and an aria-level attribute with a value from 1 to 6 to the element.

Note: aria-level is required on role="heading". If it is missing, the spec defines a fallback of level 2, but browsers and screen readers may not all follow it. Always set aria-level to match the heading's place in the hierarchy.

Pass Patterns

Native heading element

The section title is an <h2>, so it is exposed as a level 2 heading.

HTML
<h2>Shipping options</h2>
<p>Choose standard or express delivery.</p>

Heading role with aria-level

The <div> has role="heading" and aria-level="2", so it is exposed as a level 2 heading.

HTML
<div role="heading" aria-level="2">Shipping options</div>
<p>Choose standard or express delivery.</p>

Fail Patterns

Styled <div> used as a heading

The text looks like a heading, but it is a generic element, so assistive technology does not expose it as a heading.

HTML
<div class="section-title">Shipping options</div>
<p>Choose standard or express delivery.</p>

Bold paragraph used as a heading

Bold text inside a <p> is still a paragraph, not a heading.

HTML
<p><strong>Shipping options</strong></p>
<p>Choose standard or express delivery.</p>

WCAG techniques

Sufficient techniques

Failure techniques

Try It Yourself

Using a screen reader

New to screen reader testing, or need a refresher?
Review this article first: Practical guides for using screen readers

  1. Load the page and open a screen reader.
  2. Open the screen reader's headings list, or use its heading navigation key (for example, H in NVDA and JAWS).
  3. Compare the headings you hear with the text that looks like a heading on screen.

Expected results: Every visual heading appears in the headings list, with a level that matches its place in the hierarchy.

Actual results: Some visual headings are missing from the headings list and cannot be reached with heading navigation.

Inspecting the browser dev-tools

  1. Inspect the text that looks like a heading in your browser's dev tools.
  2. Open the Accessibility panel and check the element's role.

Expected results: The role is heading, with a level from 1 to 6.

Actual results: The role is generic, paragraph, or static text.

FAQ

How does this issue relate to 1.3.1 Info and Relationships?

SC 1.3.1 requires information and structure conveyed through presentation to also be programmatically determinable. When text is styled as a heading but not marked up as one, the page structure is visible on screen but missing in code, so the criterion is not met.

Why is this issue marked as Moderate?

The content itself is still available, so users can read the page. However, screen reader users lose the heading structure and the ability to jump between sections, which makes long pages slow and difficult to navigate, so it is rated moderate.

Should I use role="heading" or a native heading element?

Prefer native <h1> to <h6> elements. They are supported everywhere and need no extra attributes. Use role="heading" with aria-level only when you cannot change the element, for example in generated markup you don't control.

Standards and Regulations

StandardSuccess CriterionReference
WCAG 2.21.3.1 Info and Relationships (A)Information, structure, and relationships conveyed through presentation can be programmatically determined or are available in text.
EN 301 5499.1.3.1 Info and RelationshipsSame as the WCAG text.
508 Trusted Tester1.3.1-heading-determinable, 10.BEach programmatically determinable heading is a visual heading and each visual heading is programmatically determinable.

Personas impacted by this issue