Styled Semantic Headings

Styled Semantic Headings with CSS Utility Classes

A couple of years ago we decided to try a different approach to applying heading styles in our projects at Storyware. We stopped styling heading elements directly and started defining CSS utility classes for each stylistic need; much like you would see in an Atomic CSS workflow.

Many novice developers tend to use heading elements (h1, h2, h3, etc.) based on the desired stylistic result, and not the semantic meaning. For example, the heading on a main page may have a font-size of 36 pixels, and then an element elsewhere on the page with no true semantic meaning in terms of document flow (like a subheader), may require a font size of 60 pixels. It may be tempting for a developer to use an h2 for the main heading, and then an h1 to achieve that larger font size further down the page. But applying heading tags based purely on stylistic needs will result in a page with confusing markup that can derail SEO and accessibility.

In other cases, you may see developers overriding base heading styles with classes such as “Page-heading” or “Subtitle”. This is a better approach as it is healthy for SEO and Accessibility, but it can lead to applying class names that don’t necessarily make sense when reading through HTML. We used to employ a similar approach at one time, but feel that maintainable CSS classes should be named in a way that implies purpose, not location within the Document Object Model (DOM).

Integrating an Atomic Workflow

Our new, integrated Atomic workflow has resulted in a collection of highly specific utility classes for font size, weight, and family. Here’s an abridged example of this in Sassy CSS:

.u-heading1,
.u-heading2,
.u-heading3 {
  line-height: 1.3;
}

.u-heading1 {
  font-size: 48px;
}

.u-heading2 {
  font-size: 36px;
}

.u-heading3 {
  font-size: 32px;
}

.u-font1{
  font-family: $font-1;
}

.u-font2{
  font-family: $font-2;
}

.u-weightLight{
  font-family: 200;
}

.u-weightNormal{
  font-family: 400;
}

.u-weightBold{
  font-family: 600;
}

 

And here is how we might use this in a view:

<h3 class="u-heading1 u-weightBold u-font2">H3 heading that looks like an h1</h3>

The result above makes sense in terms of SEO and Accessibility, and is very readable for developers. This Atomic approach also enables us to move quickly when building new views, as we are able to use our utility classes much like legos. This approach is also very DRY in that we do not need to repeat CSS for text related styles throughout our SCSS files.

Inspiration

I first saw a talk on using an Atomic CSS workflow at Full Stack Toronto in 2015. Unfortunately 3 years later I don’t recall who gave the talk, but I do recall how I felt when I saw the results. The presenter showed us how he was able to drastically decrease the weight of a Fortune 500 company’s SaaS product’s codebase through Atomic utility classes. As a by-product, his team also spent far less time cleaning up CSS and more time building features.

After the talk I began looking into frameworks such as BASS CSS and Tachyons for inspiration. I also drew a lot of inspiration from a great article on Expressive CSS by John Polacek.

A few weeks later we implemented the text-styling approach outlined in this article and never looked back. Soon we began to apply more of an Atomic approach to stylistic needs such as margin, padding, and colors. The result has been an increase in productivity, more reusable code, and stylesheets that are 30-50% of the size of older, more traditional CSS workflows.

Do we use Atomic CSS for everything?

Absolutely not. We’ve been using the SUIT CSS naming convention to create components for years. Maintaining a complex UI component such as a filter or a card requiring special hover events with Atomic CSS classes just isn’t practical in most cases and this is where we still use SUIT.

We also find SUIT-style modifier classes very useful for determining theme styles within components. For example, a content block might have a variety of background styles. The admin user then chooses the dark style and we then can use this choice to create a modifier class that automatically applies other styles within the block. Here is some example SCSS for this use case:

.ContentBlock {
  background-color: #fff;
  color: #000;

  .ContentBlock-Cta {
    border-color: #000;
    color: #000;
  }
}

.ContentBlock--dark {
  background-color: #000;
  color: #fff;

  .ContentBlock-Cta {
    border-color: #fff;
    color: #fff;
  }
}

Take it for a test drive

There you have it, a peak at our CSS workflow and part of how we strive to deliver high-performing web applications while checking the boxes for performance and accessibility, without the maintenance hassles. If you aren’t taking an atomic approach to your heading styles, then take this workflow for a spin and see what you think. I’d bet it results in more building and less wrangling or fixing.