DEV Community

Praise Agbabiaka
Praise Agbabiaka

Posted on

A broken heading hierarchy is an architecture problem, not a markup one

On a recent BigCommerce remediation, an audit flagged the heading hierarchy. Levels skipped and jumped across the page, so the structure a screen-reader user navigates by didn't hold together.

The checklist fix is obvious. Open each template, retag the headings, close the finding.

But the levels weren't the bug. They were the symptom.

Each heading was hardcoded into its own template. A section rendered an <h2> because whoever wrote that template picked <h2>, with no view of where it would sit on the assembled page. Nothing owned the hierarchy. It emerged, wrongly, from dozens of local decisions.

That's the real problem, and it's architectural.

A reusable heading component has no inherent level. The same block might be an <h2> on one page and an <h3> nested inside a region on another. Its correct level depends on where it sits, not on what it is.

Any component that bakes in a fixed level is going to be wrong somewhere because it can't see its own context.

So the fix isn't to hardcode a different level. It's to stop hardcoding heading levels altogether and render each heading from one place that receives its level from whatever owns the page structure.

The principle is portable. The idiom changes per stack:

  • BigCommerce (Handlebars): a single partial that receives the level as a parameter, replacing heading markup repeated across a dozen templates.
  • Shopify (Liquid): a snippet that takes the level through render.
  • React: pass the level as a prop, or use context when the hierarchy needs to be derived from component composition.
  • Astro: let the parent own the structure and pass the level through props, rather than having the child commit to a level it can't know from its own context.

Different mechanisms, one move.

Headings render in a single place, and the level is an input from context, not a constant.

It's also the fix that lasts. A hardcoded level regresses the moment a section is reused or a template is copied. A contextual one stays correct because the decision lives with the page structure instead of being re-decided everywhere.

That's the difference between closing a finding and fixing the system that produced it.

The accessibility failure was in the markup. The fix was in the architecture.

Top comments (0)