Building an Accessible, Re-usable Heading Component
HTML headings are not semantically complex elements though they are frequently used inappropriately by both designers and engineers alike.
Why Use Headings?
Each heading can be specificied with a level (1 through 6) and render text content to the DOM. Generally, headings are used to group content in a hierarchical fashion, much like a newspaper or magazine article.
<h1>Man Lands on the Moon!</h1>
<h2>Who Landed?</h2>
<h3>The First Man on the Moon</h3>
<p>Neal Armstrong!</p>
<h3>The Rest</h3>
<p>Buzz Aldrin was also there!</p>
<h2>Were There Aliens?</h2>
<p>Sadly, no.</p>
In a traditional document layout, heading levels correspond naturally with the flow of content. This document flow helps users quickly scan for content visually and helps screen reader users navigate quickly around a page. WebAIM wrote a great article on the subject.
Common Pitfalls
Both visual designers and developers make some common mistakes when using headings in their work, largely stemming from the typical connection between style and semantic meaning.
For better or for worse, modern web applications do not tend to contain long blocks of structured content, making maintaining a strict heading hierachy more difficult. Visual designers may, for example, hope to use a heading that looks like a level 5 heading when, according to the document hierarchy, a level 2 heading might be more appropriate.
Similarly, developers will often (inappropriately) leverage headings as a means
of styling. Without enough knowledge of the importance of the heading hierarchy
of a document, an engineer may reach for an <h6 />
when a <p />
with bold
styling was needed.
Abstraction to the Rescue
Fortunately, building a re-usable Heading
component abstraction can help
separate the visual presentation from the semantic meaning of a heading instance
without relying on manual, one-off styling.
Styling
First, let's apply some styling to headings globally that will apply directly to HTML elements. Generally, styling HTML elements globally should be avoided, however, styling headings in this manner can help ensure authored content (possibly provided via a CMS) renders with the relevant hierarchical presentation.
Take the following CSS, for example, with font size values generated from modularscale.com:
h1,
h2,
h3,
h4,
h5,
h6 {
font-weight: 700;
font-family: serif;
}
h1 {
font-size: 2.986rem;
}
h1 {
font-size: 2.488rem;
}
h3 {
font-size: 2.074rem;
}
h4 {
font-size: 1.728rem;
}
h5 {
font-size: 1.44rem;
}
h6 {
font-size: 1.2rem;
}
Providing Semantic Control
With styling handled, now we need a re-usable way to generate the heading level from a single component:
import React from 'react'
import './heading-styles.css'
function Heading({ as, children }) {
const Component = as
return <Component>{children}</Component>
}
function MyPage() {
return (
<>
<Heading as="h1">My Page</Heading>
<Heading as="h2">More Information</Heading>
</>
)
}
Separate Form from function
With the DOM rendering handled, now we incorporate a new prop looksLike
that
helps divorce presentation from the underlying semantics. In this example, we
leverage the
ARIA heading role
paired with the aria-level
attribute in order to have more granular control
over the DOM semantically:
import React from 'react'
import './heading-styles.css'
function Heading({ as, looksLike, children }) {
const Component = looksLike ? looksLike : as
const headingLevel = as.replace('h', '')
return (
<Component role="heading" aria-level={headingLevel}>
{children}
</Component>
)
}
function MyPage() {
return (
<>
<Heading as="h1">My Page</Heading>
<Heading as="h2" looksLike="h3">
More Information
</Heading>
</>
)
}
Now, the "More Information" heading looks like a level 3 heading, but is semantically a level 2 heading.
Conclusion
Building a re-usable, accessible component abstraction can help improve the developer experience of a project while also improving the end-user experience. Spending a little time to introduce such an abstraction can pay off in the long run, allowing for easier feature development, all while lowering the risk of accessibility defects.
About Me
I’m a frontend engineering manager working for Truist Financial currently based in in Columbia, Maryland.
I’m driven to spearhead, design, and build accessible design systems and web applications with a great underlying developer experience in mind.