A navigable outline, not just bold running text
A correct heading structure from h1 to h6 turns a web page into a navigable table of contents for screen reader users, who jump from heading to heading with a keyboard shortcut. Skipping levels just for font size quietly breaks that navigation. This article covers the practical one h1 convention, correct hierarchies and fast audit methods for Magento and Hyva stores.
Table of Contents
- 1. Why heading structure determines navigability
- 2. Semantic meaning versus visual styling
- 3. Exactly one h1 per page: the practical convention
- 4. Keep the hierarchy: never skip levels
- 5. Implementation in Magento and Hyva templates
- 6. Working together with ARIA landmarks and roles
- 7. Auditing: browser tools and screen reader testing
- 8. Common mistakes in practice
- 9. Heading patterns compared side by side
- 10. Summary
- 11. FAQ
1. Why heading structure determines navigability
Sighted users skim a page visually: large bold text signals a new section, smaller sub headings organize the details underneath. Screen reader users do not have that visual shortcut. Instead they rely entirely on the programmatic heading structure in the HTML. Assistive technologies like JAWS, NVDA and VoiceOver each offer a dedicated shortcut, usually the H key, that lets users jump from one heading to the next without listening to the entire page read aloud. WebAIM surveys have consistently shown for years that the majority of screen reader users name this exact jump navigation as their preferred method for getting a first overview of an unfamiliar page.
That makes the heading structure the page's actual table of contents, not just a typographic formatting choice. When a genuine hierarchy is missing or inconsistent, the screen reader user loses precisely that orientation, while the page looks completely unremarkable to sighted visitors. The mistake usually goes unnoticed day to day because no visual break occurs, only a structural one. That very invisibility is what makes heading mistakes one of the most common, and at the same time one of the most easily avoidable, barriers on the web.
2. Semantic meaning versus visual styling
The central thinking error with headings: developers pick the tag level h1 through h6 based on the desired font size, not based on the content's actual position in the document. An h4 gets used because it looks smaller than an h2 in the default stylesheet, even though the section is clearly a second level in the outline. Sighted users never notice, but for the accessibility API that communicates structure to screen readers, it is a direct break: the level in the document tree no longer matches the actual content hierarchy.
The clean solution separates the two responsibilities strictly: the HTML level h1 through h6 describes only the position in the content tree, while CSS classes control the visual size, weight and spacing. An h2 is allowed to look just as small as an h5, as long as the semantic level matches the actual outline. Tailwind utility classes are particularly well suited for this, since they decouple size and tag completely without needing separate CSS selectors per heading level.
<!-- Semantic level and visual size are two different things -->
<!-- WRONG: h5 chosen only because the text should look smaller -->
<h5 class="font-bold text-2xl mt-8">New Summer 2026 Collection</h5>
<p>Lightweight fabrics, clean cuts, limited runs.</p>
<!-- RIGHT: h3 keeps the correct level in the document tree, -->
<!-- the size comes purely from the utility class -->
<h3 class="font-bold text-2xl mt-8">New Summer 2026 Collection</h3>
<p>Lightweight fabrics, clean cuts, limited runs.</p>
3. Exactly one h1 per page: the practical convention
The HTML5 specification technically allows multiple h1 elements per page, as long as they sit inside separate sectioning elements like article or section and a so called outline algorithm derives the levels from that nesting. In practice, no mainstream browser and no screen reader fully implements this theoretical outline algorithm. For JAWS, NVDA and VoiceOver users, what counts is simply the actual tag level in the DOM, regardless of the surrounding sectioning element.
That is why a simple, robust convention has taken hold in practice: exactly one h1 per page, naming the page title or the central topic, such as the product name on a product detail page or the category name on a category page. Every further section starts at h2 and works downward. This convention is not an official WCAG requirement, but it is the most reliable way to give screen reader users an unambiguous answer to where they currently are, the moment they press the 1 key to jump straight to the next h1.
4. Keep the hierarchy: never skip levels
Alongside the one h1 convention, the second core rule is this: heading levels must never be skipped when descending. An h2 may only be followed by an h3 as the next deeper level, never directly by an h4. The reason again lies in jump navigation: a screen reader user working systematically from level to level expects an unbroken chain. When an intermediate level is missing, it creates the impression that an entire section has vanished, even though the content is actually there, just filed under the wrong level.
Jumping back up to a higher level, by contrast, is unproblematic: after several h3 sections, moving straight back to a new h2 is the normal, correct way to close out a subsection. The rule only concerns descending in the hierarchy. Anyone planning a redesign who wants a smaller font size for a specific area should always ask the content question first: what is the actual outline level of this section, regardless of how it should eventually look.
5. Implementation in Magento and Hyva templates
In Magento stores, heading mistakes tend to appear in three places: the theme layout itself, CMS blocks maintained through the WYSIWYG editor, and widget output from third party modules. The WYSIWYG editor in the Magento backend lets editors pick any heading level freely, without regard for the surrounding page structure. The result: a CMS block with an h2 right in the middle of a product detail page that already has its own h2 for "Description", or a marketing widget that starts with h1 out of habit.
In Hyva Theme, structure can be controlled directly in the phtml template, since there is no block generated markup like in classic UI Components. The product detail page sets the product name as the single h1, while sections like description, technical data or reviews consistently start at h2. Editors additionally benefit from a CMS guideline that restricts WYSIWYG headings to start at h3, so that editorial content can never collide with the page structure.
<!-- Hyva phtml: category page with a correct heading hierarchy -->
<div class="category-view">
<h1 class="text-3xl font-bold"><?= $block->escapeHtml($category->getName()) ?></h1>
<?php if ($block->getCategoryDescription()): ?>
<div class="category-description mt-4">
<?= /* @noEscape */ $block->getCategoryDescription() ?>
</div>
<?php endif; ?>
<h3 class="text-xl font-semibold mt-8">Products in this category</h3>
<div class="product-grid">
<?= $block->getChildHtml('category.product.list') ?>
</div>
<?php if ($block->getRelatedCategories()): ?>
<h3 class="text-xl font-semibold mt-8">Related categories</h3>
<?= $block->getChildHtml('category.related') ?>
<?php endif; ?>
</div>
6. Working together with ARIA landmarks and roles
ARIA landmarks like main, nav and aside complement heading structure, they do not replace it. Landmarks answer the question "which area of the page is this", headings answer "what content outline does this area have". A screen reader user often moves back and forth between both navigation types: first jumping via landmark to the main area, then using the heading key to move through the sections there. When either level is missing, the other remains a fallback, but a considerably less precise one.
For interactive components that cannot use a native h1 through h6 for design reasons, such as a div acting as an accordion title through JavaScript, role="heading" combined with aria-level="3" can force an equivalent semantic level. This should remain the exception, though: a native h3 element is always more robust, because it works without extra ARIA attributes and is automatically recognized correctly by every tool. Automated audit tools like axe-core flag skipped levels regardless of whether they were produced natively or via an ARIA role.
{
"id": "heading-order",
"impact": "moderate",
"description": "Ensures the order of headings is semantically correct",
"help": "Heading levels should only increase by one",
"helpUrl": "https://dequeuniversity.com/rules/axe/4.9/heading-order",
"nodes": [
{
"html": "<h4 class=\"font-bold text-2xl\">New Summer 2026 Collection</h4>",
"target": [".category-view > h4"],
"failureSummary": "Fix any of the following: Heading order invalid, expected h2 or h3 but got h4"
}
]
}
7. Auditing: browser tools and screen reader testing
The fastest way to check a heading structure is a free browser extension like HeadingsMap, or the accessibility panel built into the Chrome and Firefox developer tools. HeadingsMap displays a page's entire outline at a glance as an indented tree and immediately flags skipped levels visually. Anyone who cannot or does not want to install an extension gets the same result with a few lines of JavaScript straight in the browser console, which also fits nicely into a pre deployment script.
Automated tools like axe-core or Lighthouse reliably catch structural mistakes such as skipped levels or multiple h1 elements, but they cannot tell whether a heading actually makes sense as content. That is why a short manual test with NVDA or VoiceOver rounds out the automated check: press the H key to move through the page and listen along to whether the outline read aloud actually makes sense to someone who cannot see the page. This test rarely takes longer than five minutes per page type.
// Run in the browser console: prints the page's heading outline
(function printHeadingOutline() {
const headings = document.querySelectorAll('h1, h2, h3, h4, h5, h6');
let lastLevel = 0;
headings.forEach((heading) => {
const level = parseInt(heading.tagName.substring(1), 10);
const indent = ' '.repeat(level - 1);
const skipped = level - lastLevel > 1 && lastLevel !== 0;
const marker = skipped ? ' <-- level skipped' : '';
console.log(`${indent}h${level}: ${heading.textContent.trim()}${marker}`);
lastLevel = level;
});
const h1Count = document.querySelectorAll('h1').length;
console.log(`\nFound h1 elements: ${h1Count}${h1Count !== 1 ? ' <-- should be exactly 1' : ''}`);
})();
8. Common mistakes in practice
The most common mistake is choosing the heading level for purely visual reasons, closely followed by div or span elements styled bold and large with CSS to look like a heading, while remaining completely unrecognizable as a heading semantically. For screen reader users, such an element simply does not exist in the outline, no matter how prominent it looks visually. Another classic mistake comes from several independently built components on the same page, for example a marketing banner module that starts with h1 by default, combined with the actual page h1 from the theme.
A third common mistake: headings get misused for purely decorative marketing claims, such as <h2>Grab the deal now!</h2> with no real content section underneath. That artificially inflates the outline for screen reader users and dilutes the actual structure. The rule of thumb: a heading always describes the content block that follows it, never just an isolated piece of marketing copy with no associated section.
/* Define size classes independently from the semantic level */
.heading-display { font-size: 2.25rem; font-weight: 800; line-height: 1.2; }
.heading-section { font-size: 1.5rem; font-weight: 700; line-height: 1.3; }
.heading-label { font-size: 1rem; font-weight: 600; letter-spacing: 0.02em; }
/* An h3 is allowed to look visually like an h1, without changing the level */
h3.heading-display {
font-size: 2.25rem;
}
/* A div looks like a heading, but semantically it is not one */
/* Screen reader users will not find this element in the outline */
.fake-heading {
font-size: 1.5rem;
font-weight: 700;
}
9. Heading patterns compared side by side
The table below summarizes the most important decisions between an unreliable and a correct implementation. Each row describes a scenario that shows up regularly in Magento and Hyva stores.
| Scenario | Wrong | Right | Benefit |
|---|---|---|---|
| Smaller text desired | h4 instead of h2 for looks |
h2 with a CSS class for a smaller size |
Semantic level stays correct |
| Multiple page areas | Every block starts with h1 |
Exactly one h1, the rest h2 through h6 |
Unambiguous entry point for screen readers |
| Subsection nested deeper | h2 directly followed by h4 |
h2 followed by h3 |
Unbroken jump navigation |
| Title needs to stand out | <p><strong>Title</strong></p> |
<h3>Title</h3> |
Shows up in the heading outline |
| Marketing claim with no section | <h2>Grab it now!</h2> with no content below |
Claim as <p>, h2 only for real sections |
Outline stays content wise clean |
All five scenarios share the same core idea: the choice of heading level should always follow the actual content position in the document, never the desired appearance. Anyone who consistently keeps that separation never has to rebuild headings after the fact when the design changes.
Mironsoft
Web accessibility, WCAG audits and Hyva Theme development for Magento stores
Is your heading structure actually navigable?
We check the heading hierarchy of your Magento or Hyva store with screen reader tests and automated audits, and fix skipped levels, duplicate h1 elements and CMS blocks that break the structure.
Heading audit
Full outline review with axe-core and a manual screen reader test
Theme refactoring
Rebuilding Hyva templates around a clean h1-h6 structure
CMS guidelines
Establishing editorial guidelines for WYSIWYG headings in the backend
10. Summary
A correct heading structure is not a question of looks, it is the page's table of contents for screen reader users who jump straight from one heading to the next with a keyboard shortcut. h1 through h6 describe only the position in the document tree, while CSS classes control size and style. Exactly one h1 per page has become the robust convention, because no mainstream screen reader reliably implements the theoretical HTML5 outline algorithm. Descending into deeper levels must never skip a step, while ascending back to a higher level is always fine.
In Magento and Hyva stores, mistakes usually appear in three places: the theme itself, CMS blocks edited through the WYSIWYG editor, and third party widgets. Browser extensions like HeadingsMap, automated tools like axe-core and a short manual test with NVDA or VoiceOver catch the most common problems within minutes per page type. Anyone who consistently keeps semantic level and visual size separate never has to touch the structure again with the next redesign.
Using Heading Structure Correctly (h1-h6): The Essentials at a Glance
Semantics before looks
The heading level follows the content's position in the document. Size and style are controlled by CSS alone.
Exactly one h1
One h1 per page as a robust convention, since no screen reader fully uses the HTML5 outline algorithm.
Never skip levels
Always use the next level down when descending, never jump two steps deeper at once.
Quick to audit
HeadingsMap, axe-core and a short screen reader test catch mistakes within minutes.