Web Platform · HTML and Semantics

Sectioning Content and the Heading Outline Browsers Never Implemented

Nesting sections was meant to derive heading levels automatically. No engine shipped that, the algorithm left the standard, and headings now depend entirely on the level the author writes.

An h1 inside a section which is itself inside an article is, to every browser and every screen reader in use, a level-one heading. The design that would have made it a level-three heading existed on paper for years. It was called the document outline algorithm, it derived heading depth from the nesting of sectioning elements, and it was never implemented by an engine.

That gap between the specified model and the shipped model is worth understanding, because the sectioning elements survived while their original justification did not. What remains is a set of containers that produce landmarks, and a heading system that is entirely manual.

The outline as designed

The plan was to make heading levels relative. Each article, aside, nav, and section opened a new section in an outline. Headings inside a section were ranked by the depth of the section, not by their tag: an h1 at the top level was a top-level heading, an h1 inside one nested section became a second-level entry, an h1 inside two became third-level, and so on. h2 through h6 were interpreted as offsets from the current section depth rather than as absolute levels, so an h2 in a top-level section also produced a second-level entry.

The attraction was componentisation. A widget could ship with its own h1 and be dropped anywhere without the host page rewriting heading tags, and the outline would place it correctly. The document outline was also intended to be the structure a browser could expose for navigation, and the specification described an outline with sections, headings, and implied untitled sections for containers that had no heading of their own.

The model had an obvious extension: if every section has exactly one heading and nesting determines rank, then h1 alone is sufficient and the numbered tags become noise. That was the argument made publicly for years, and it is the recommendation that turned out to be wrong.

Why the algorithm was withdrawn

Two problems killed it. The first is that no engine implemented it, so the specified outline and the observable outline disagreed for every document. Authoring advice based on the specification produced documents that assistive technology ranked differently from the way the advice promised.

The second is that the outline was meant to feed the accessibility tree, and that mapping is not something an engine can change unilaterally while keeping compatibility. A heading element maps to a heading role with a level, and the level comes from the tag name and from aria-level when present. Changing the mapping for nested sections would have reordered heading navigation for existing pages.

The algorithm was removed from the HTML Standard rather than deprecated in place. The sectioning elements kept their definitions, their ARIA mappings, and their scoping behaviour for header and footer; the outline chapter that would have given them heading semantics went away. hgroup followed a similar path: it was removed from the standard, then reintroduced with narrower semantics, grouping a heading with one or more paragraphs that serve as its subtitle rather than changing heading rank.

What sectioning content still does

Four elements define sectioning content: article, aside, nav, section. Their remaining jobs are structural and semantic.

article maps to the article role and represents a self-contained composition that could stand alone in a feed. aside maps to complementary, a landmark for tangentially related material. nav maps to navigation whether or not it has a name, though an unnamed navigation landmark is harder to tell apart from the others. section maps to generic unless it carries an accessible name, in which case it maps to region. That last mapping is easy to over-apply: a page with fifteen named sections exposes fifteen regions in the landmark list, which is worse for navigation than exposing none. The name is what makes a region useful, and the count is what makes it noise.

Sectioning content and main also define the scope of two other elements. A header is a banner landmark only when it is not nested inside article, aside, main, nav, or section; inside an article it is the article’s header and has no landmark role. A footer is contentinfo only outside those same elements. This is why a page can have several header elements and exactly one banner: the scoping rule decides which one it is.

None of this depends on headings. A section with no heading is still a container and still scopes its header, and a heading outside any sectioning element still produces a heading node in the accessibility tree with the level its tag names.

Headings as literal levels

Since the level is the tag, the rules for correct use are rules about sequence and intent rather than about nesting.

A document should have a heading structure that descends without skipping when the content is genuinely nested. The accessibility guidelines treat skipped levels as a quality problem rather than a hard failure: a page that goes from h1 to h4 is still navigable, but a reader who moves by heading level loses the sense of depth that the levels encode. Multiple h1 elements on one page are permitted by the standard and are common in pages assembled from independent articles; whether they read well depends on whether each one is genuinely a top-level title, and on whether the surrounding landmarks make the boundary clear.

Custom elements that act as headings cannot borrow the tag. They need role="heading" together with aria-level, and both are required for the role to be useful: a heading role with no level is exposed with a default level that does not reflect the document structure. The same approach applies to elements whose semantics were removed by CSS or by a component library, and it is the only mechanism that survives the absence of the outline algorithm.

Headings also need content. An empty heading element is exposed as an empty heading, which appears in the heading list as a blank entry between two real ones. The fix is to remove the element rather than to fill it with a character, since the character becomes part of the announced name.

Constructing a page that has both

The durable structure is a single main landmark, a nav with a name that says which navigation it is, and headings that reflect the actual hierarchy of the content. Sectioning elements are useful where they carry one of those meanings: an article for a post, an aside for a pull quote or a sidebar, a section with a name for a distinct part of a long document, a nav for a group of links.

What does not work is using section as a generic wrapper on the theory that it deepens the heading structure. It does not deepen anything, it adds an unnamed container, and it adds a node that a validator will flag if it has no heading and no other indication of what it contains. A div is the correct element for a wrapper whose only purpose is styling or scripting, and the difference between div and section is exactly the presence of a thematic heading in the author’s intent.

Neither does it work to assign heading levels by component. A card component that hard-codes an h3 will produce a skipped level when it is placed directly under an h1, where the surrounding structure calls for an h2. The levels have to be decided at the point of assembly, which usually means the component accepts a level as a parameter, or renders a div and lets the caller supply the heading. That is an ergonomic cost that the outline algorithm was designed to remove, and the cost is real.

Auditing the result

The check that matches what users experience is the accessibility tree, not the markup. Browser developer tools expose the computed roles and names, and a heading list from an assistive technology or a tool that reads the same mapping shows the levels as the browser computed them. Comparing that list against the visual hierarchy catches the two failure modes that matter: a heading that is visually prominent but marked as a lower level than its parent, and a container that was expected to produce a heading but produces only an unnamed group.

The same inspection shows the landmark structure. A page with a banner, a navigation, a main, and a contentinfo, plus a small number of named regions, is navigable at a coarse level even before headings are considered.

Semantic structure that depends on an unimplemented algorithm is not structure at all. The sectioning elements are containers with landmark and scoping behaviour; the heading level is whatever number is on the tag or in aria-level. Everything else about document structure is a convention that has to be maintained by the author, and the conventions that survive are the ones an engine can expose.

The next question in the same layer is how form controls communicate their validity, which the browser does implement, and where the constraint rules are precise enough to build against: Constraint Validation and the Form Validity State Machine.

References

  1. HTML Standard — Sections — WHATWG
  2. ARIA in HTML — W3C
  3. Web Content Accessibility Guidelines 2.2 — W3C