Styling headings is either a deceptively complex problem, or maybe the design of CSS made it appear complex when it need not have done.
When styling headings (or really anything) we want three big goals to be met:
- DRY – Do not repeat yourself. We want to set headings once and never (ok, rarely!) repeat those font styles or selectors. This will make our site easier to maintain.
- Predictable – The heading should look the same no matter where on the page it is placed. This will make creating new pages and content easier.
- Keep specificity low and selectors as simple as possible. This will help with performance and keep the site from growing more and more tangled over time.
The html5 section tag is weird. It dramatically changes the way we use headings. It also changes the way browsers and assistive technologies are meant to interpret those headings. The spec says:
“The section element represents a generic section of a document or application. A section, in this context, is a thematic grouping of content, typically with a heading.”
The HTML5 Spec
The spec goes on to warn us that sections are not really meant to be used just because you want to attach a style a particular piece of content:
“The section element is not a generic container element. When an element is needed for styling purposes or as a convenience for scripting, authors are encouraged to use the div element instead. A general rule is that the section element is appropriate only if the element’s contents would be listed explicitly in the document’s outline.”
The HTML5 Spec
People seem to have blocked out this last (very important) bit, so I’ll reiterate. Sections aren’t really meant to be used by CSS for style purposes (at least not *only* for styles). So, if sections aren’t meant to be used to make my headings purty, why do they exist? They change the way the browser and assistive technologies interpret the importance of a heading relative to the other headings on the page. To understand this, you need to know a bit about the pitfalls of the way headings have been written up until now.
HTML Headings
Think back to the outlines you wrote for term papers in high school. Each bit of a website is meant to be like that, each heading corresponds to a piece of that outline, and you know when you go up or down a level by the heading level chosen. H2 is down one level from H1. And h6 is down four levels from H2.
THE TITLE IS THE H1
I. Big roman numerals are the H2s
A. This is an h3
B. This is also and h3
i. Now we have an h4
ii. And another h4
II. Big roman numerals are the H2s
III. Big roman numerals are the H2s
IV. Big roman numerals are the H2s
The trouble is, on a modern website or web app, this model isn’t a really natural fit. Especially if a site “mashes up” content from sources they don’t control (say for example adding a twitter feed to a blog), they may not be able to set the heading levels used by the mashup content. In this case, most developers, just include the new content, and the outline gets a little murkier.
A murky outline may be somewhat normal, because I’d take it a step further and say that, on most modern websites, the idea that the site has much in common with a high school term paper outline is a bit of a stretch — The section element tries to bridge the gap between the w3c’s outline view of the web and the way developers are really building sites. The section element essentially reorders the heading tree so that whatever headings are used, if they are wrapped in a section element, they will be made to fit in with other content on the page. They need only be internally consistent within each section.
<h2>Me on the web...</h2>
<h1>My Twitter Feed</h1>
<ul class="tweets">
<li>Mmmm, cornflakes.</li>
<li>Something inane...</li>
</ul>
<p><a href="more.html">More stuff on the web</a></p>
HTML5 Headings & Section Elements
If you wrap it in a section, the browser will interpret it as one level down from it’s parent heading.
<h2>Me on the web...</h2>
<section>
<h1>My Twitter Feed</h1>
<ul class="tweets">
<li>Mmmm, cornflakes.</li>
<li>Something inane...</li>
</ul>
</section>
<p><a href="more.html">More stuff on the web</a></p>
The section element also makes it clear that the list of tweets belongs to the Twitter feed, and the more link does not. This makes content more portable, which is okay — even if it maybe isn’t that important. However, it does seem to be confusing people about how they should style their headings. I think this bit of the spec might be confusing people:
Notice how the use of section means that the author can use h1 elements throughout, without having to worry about whether a particular section is at the top level, the second level, the third level, and so on.
The HTML5 Spec
This has lead people to think that they should only ever use h1s (which, is a fair interpretation of the working group’s note). However, lots of people have taken it a little too far because they didn’t read the second quote [1], where it says additional section elements shouldn’t really be used only to apply CSS styles. Admittedly a subtle difference, but important! Section elements are meant to help the browser figure out what level the heading really is, but not necessarily to decide how to style it. By tying styles to browser heading level interpretation, developers (trying to implement html5 from the spec) are ending up with selectors that look like this:
h1{font-size: 36px}
section h1{font-size: 28px}
section section h1{font-size: 22px}
section section section h1{font-size: 18px}
section section section section h1{font-size: 16px}
section section section section section h1{font-size: 14px}
section section section section section section h1{font-size: 13px}
section section section section section section section h1{font-size: 11px}
(Note: This is vastly simplified as I’ve only included sections and not the other sectioning elements like articles
or asides
. This is a more realistic, real life code sample.)
Let’s see how well this meets our goals:
Q: What if, semantically speaking, you need to add an additional section to a bit of html?
It will unintentionally change the way your headings look. If it is high enough on the document tree it could change your entire page. That seems badly unpredictable.
Q: What happens if the design calls for a 14px heading in a part of the site that is only nested two sectioning contents deep?
To make it work with the existing code, you would need to add additional unnecessary section elements. The spec pretty clearly states that section elements are not meant to be added just to change styles. Plus, that is just kind of gross.
Q: What if we create another rule that duplicates the 14px property value pair?
section.special section h1 {font-size:14px}
This clearly isn’t DRY. We’re repeating the code to set the font-size to 14px, and our specificity is starting to get weird. If we want a normal two-section deep heading (22px) we now can’t have it in the special section. We also can’t reuse the new rule anywhere else. Continue in this direction for a while on a project and you can end up with hundreds or even thousands of heading declarations. Eek! This isn’t meeting our stated goals at all.
(For more info about how this can get out of control, check out this video on how our current methods for styling headings are leading to bad outcomes.)
So, how do we style headings in an HTML5 world?
The answer is right there in the spec, next to the place where we learned to use only H1s. We shouldn’t use sectioning elements for styling. We should let them do the job they were designed for, which is sorting out the document tree, and solve styling issues another way that meets our goals better: with simple reusable class names that can be applied to any of our headings no matter how deep they may be in the sectioning content.
I recommend abstracting site wide headings into classes because then they are portable, predictable, and dry. You can call them anything you like. Jeremy Keith recommends something like:
.Alpha {}
.Beta {}
.Gamma {}
.Delta {}
.Epsilon {}
.Zeta {}
or
.tera {}
.giga {}
.mega {}
.kilo {}
.hecto {}
.deca {}
.deci {}
.centi {}
.milli {}
.micro {}
.nano {}
.pico {}
I keep it simple with:
.h1{}
.h2{}
.h3{}
.h4{}
.h5{}
.h6{}
It doesn’t really matter what system you choose as long as it is something easy for your team to remember. Then, no matter how many section levels deep your heading is nested, you can make it look just how you want:
<h1 class="giga">Me on the web...</h1>
<section>
<h1 class="kilo">My Twitter Feed</h1>
<ul class="tweets">
<li>Mmmm, cornflakes.</li>
<li>Something inane...</li>
</ul>
</section>
<p><a href="more.html">More stuff on the web</a></p>
So now, your twitter feed, from the previous example, can be in the sidebar on an article page, or in the footer of your homepage, and it will still look the way it was designed to. Whether you are using html5 sections or not, you don’t want to repeat code or have it be unpredictable, so I think separating styles from how the browser generates the page outline is just sensible.
UPDATE 9/6: If you truly cannot change the HTML, even to add class names, then the only way to style that bit is to put a wrapper around it with a unique class and use descendent selectors to force the style you want. This should be the exception, not the rule! (Thanks Simon for pointing out that I wasn’t addressing one of the use cases I had talked about above)
<h1 class="giga">Me on the web...</h1>
<section class="tweetfeed">
<h1>My Twitter Feed</h1>
<ul class="tweets">
<li>Mmmm, cornflakes.</li>
<li>Something inane...</li>
</ul>
</section>
<p><a href="more.html">More stuff on the web</a></p>
If you would like a far more eloquent walk through all the new html5 elements, get Jeremy Keith’s book HTML5 For Web Designers.
Thanks to Alex Kessinger and Josh for helping me solidify my thoughts around this by bringing up good questions on the CSS Lint Google Group.