HTML Heading Levels and Sectioning Content
You may have noticed that there is no difference in size between an h1
heading
and h2
heading within an article
or section
element.
That’s weird. In this article, I’ll explain why it is, and the current recommendation for HTML heading levels.
Headings as Children of the body
Element
First, let’s look at heading levels that are direct children of the body
:
<body>
<h1>Heading Level 1: No Nesting - at Root</h1>
<h2>Heading Level 2</h2>
<h3>Heading Level 3</h3>
<h4>Heading Level 4</h4>
<h5>Heading Level 5</h5>
<h6>Heading Level 6</h6>
</body>
Result: Headings get consistently smaller from 32px to 10.72px (assuming a base font size of 16px):
Headings as Children of a section
Element. No Nesting.
Next, let’s take that same structure and throw it in a section
element:
<body>
<section>
<h1>Heading Level 1: No Nesting - Decreasing Levels</h1>
<h2>Heading Level 2</h2>
<h3>Heading Level 3</h3>
<h4>Heading Level 4</h4>
<h5>Heading Level 5</h5>
<h6>Heading Level 6</h6>
</section>
</body>
Result: h1
and h2
are the same size (24px) and from there
they descend in size to 10.72px (assuming a base font size of 16px):
The fact that the h1
and h2
are the same size is surprising and I believe is the result of an
attempt to account for how web developers have acted in the past and how the standards bodies wished they would have acted.
Before I get into that though, let’s discuss structure for a moment. Consider the following code:
<body>
<section>
<h1>Fruit and Vegetables</h1>
<p>Fruit and vegetables are good for you.</p>
<h2>Fruit</h2>
<ul>
<li>Bananas</li>
<li>Apples</li>
<li>Grapes</li>
</ul>
<h2>Vegetables</h2>
<ul>
<li>Spinach</li>
<li>Lettuce</li>
<li>Zucchini</li>
</ul>
<p>Intelligence is knowing that a tomato is a fruit.
Wisdom is not putting tomatoes in the fruit salad.</p>
</section>
</body>
The lists are children of the sections implicitly created with the h2
elements that precede them.
The last paragraph, however, is a child of the whole section, but not a child of the section denoted by the h2
element that precedes it.
In other words, the witticism is not specific to the Fruit heading or the Vegetables heading. It has to do with both.
To make this clear, we should add new sections:
<body>
<section>
<h1>Fruit and Vegetables</h1>
<p>Fruit and vegetables are good for you.</p>
<section>
<h2>Fruit</h2>
<ul>
<li>Bananas</li>
<li>Apples</li>
<li>Grapes</li>
</ul>
</section>
<section>
<h2>Vegetables</h2>
<ul>
<li>Spinach</li>
<li>Lettuce</li>
<li>Zucchini</li>
</ul>
</section>
<p>Intelligence is knowing that a tomato is a fruit.
Wisdom is not putting tomatoes in the fruit salad.</p>
</section>
</body>
This makes it really clear that the p
element with the witticism is not a child of the
Vegetables’ section
as we can show by applying this CSS rule:
section {
outline: 1px dashed red;
margin: .5em;
padding: .5em;
}
The result:
One takeaway here is that authors should explicitly section elements and not rely on heading levels to implicitly create them. Indeed, this is recommended by WHATWG:
Authors are also encouraged to explicitly wrap sections in elements of sectioning content, instead of relying on the implicit sections generated by having multiple headings in one element of sectioning content.
Take another look at the styled screenshot above. I have not used CSS to change the size of the h1
and h2
elements.
As you can see they are the same size.
They are both 24px, which is the default size for an h2
element that is a child of the body
,
which implies that h1
and h2
elements in
sectioned content are both considered to have the rank of h2
elements. Again, this is weird.
So, why this behavior? Again, I believe it is the result of an attempt to account for how web developers have acted in the past and how the standards bodies wished they would have acted.
How Web Developers Have Acted in the Past
Because older browsers didn’t support sectioning content, when web developers started using it,
they commonly would use an h2
element for the top-level heading in a section
, article
, aside
, etc.
The purpose was to indicate that those headings were a rank below the headings that were direct children of the body
.
How the W3C and WHATWG Wished Web Developers Had Acted
If the creators of the specifications could, I think they would like to magically change every top-level heading within sectioning content
from an h2
to an h1
and then let the browsers use the nesting structure to decide on heading rank.
For example, a body>section>h1
would be ranked lower than a body>h1
.
In this scenario, the preferred structure would have the ranking decrease with every new section.
Authors should then explicitly section elements and not rely on heading levels to implicitly create them.
In this case, there is little need for h2
elements and lower:
<body>
<section>
<h1>Heading Level 1: Nesting - All h1 Headings</h1>
<section>
<h1>Heading Level 1</h1>
<section>
<h1>Heading Level 1</h1>
<section>
<h1>Heading Level 1</h1>
<section>
<h1>Heading Level 1</h1>
<section>
<h1>Heading Level 1</h1>
</section>
</section>
</section>
</section>
</section>
</section>
</body>
Modern browsers support this structure and will treat each h1
in a nested section as a lower rank than the h1
in the outer section.
Here’s the output:
Notice that the bottom two headings are the same size.
That is because the h1
in the outermost section
element is treated as a 2nd-level heading.
As headings only go six levels, both headings in the fifth and the sixth nested level sections are treated as 6th-level headings.
Now let’s see what happens when we use lower-level headings for each nested section
:
<body>
<section>
<h1>Heading Level 1: Nesting - Decreasing Levels</h1>
<section>
<h2>Heading Level 2</h2>
<section>
<h3>Heading Level 3</h3>
<section>
<h4>Heading Level 4</h4>
<section>
<h5>Heading Level 5</h5>
<section>
<h6>Heading Level 6</h6>
</section>
</section>
</section>
</section>
</section>
</section>
</body>
In this case, the outermost section
’s h1
is still treated like a 2nd-level heading, but so is the second level’s h2
element!
See below:
This is super weird! To show just how weird, consider the result of this code:
<body>
<section>
<section>
<h1>Heading Level 1</h1>
<h2>Heading Level 2</h2>
</section>
</section>
</body>
The result:
A section>section>h2
is treated as a 2nd-level heading while a section>section>h1
is treated as a 3rd-level heading!
While this seems completely nuts to me, it is the recommended approach of WHATWG,
except that they recommend you begin with an h2
element as the top-level heading of sectioning content. Here’s the example they give:
<body>
<h1>Apples</h1>
<p>Apples are fruit.</p>
<section>
<h2>Taste</h2>
<p>They taste lovely.</p>
<section>
<h3>Sweet</h3>
<p>Red apples are sweeter than green ones.</p>
</section>
<section>
<h3>Color</h3>
<p>Apples come in various colors.</p>
</section>
</section>
</body>
Two things I don’t like about this:
- If I decide to add another level of nesting in the middle of the tree somewhere, I need to change the heading levels of the lower nested sections.
- If the content is syndicated in some way and inserted in someone else’s tree, it will get messed up. Consider the following:
This will output: Using all<body> <section> <h2>Heading Level 2</h2> <section> <h3>Heading Level 3</h3> <p>The content below is pulled from somewhere else:</p> <article> <!-- Syndicated content --> <h2>Heading Level 2</h2> <section> <h3>Heading Level 3</h3> </section> </article> </section> </section> </body>
h1
elements for headings would fix this:
This will output:<body> <section> <h1>Heading Level 1</h1> <section> <h1>Heading Level 1</h1> <p>The content below is pulled from somewhere else:</p> <article> <!-- Syndicated content --> <h1>Heading Level 1</h1> <section> <h1>Heading Level 1</h1> </section> </article> </section> </section> </body>
My Recommendation?
My recommendation is to go with the recommended approach in the specification, because who am I to tell you to do it differently. Then use a normalizer like normalize.css, which does not base the size of the heading level elements on their location in the document tree. Then use your own CSS to set the sizes you want.
Related Articles
- HTML Heading Levels and Sectioning Content (this article)
- How to Ask Good Technical Questions
- How to Install and Use Visual Studio Code for Class
- How to Open HTML Files in Your Browser from Visual Studio Code
- How to Force a Refresh of favicon.ico