BEM + utilities: a hybrid methodology

Please forgive the snappy title, and allow me a few hundred words of backstory before we get into the meat here. Or just skip to the next content heading if you're in a hurry.

A few hundreds words of backstory

I write a lot of CSS.

Very little of it is tied to any specific library. I never learned Bootstrap or Foundation (cool URL, Zurb). I used Bulma for a little while, for prototyping, but never learned how to customise a theme. I read a bit about Tachyons when I learned that Heroku was using it, but then again Heroku uses Ember so what do I know. When I first heard about Tailwind, I thought it was basically dumb to inline all of your styles. Why not just write all of your CSS in style tags?

But on a podcast at some point (I don't remember which) I heard someone (I don't remember who) talking about how they had to maintain a legacy site, and every time they edited a bit of existing CSS they messed with something somewhere else. They couldn’t be sure that editing a rule for a single use case wouldn’t break some use case elsewhere. And I could identify.

Then they contrasted it with the utility-based CSS approach. If you're not familiar, utility-based CSS frameworks deliver a bevy of classes that map reasonably closes to individual CSS rules. Like:

.uppercase { text-transform: uppercase; }
.text-left { text-align: left; }
.text-primary { color: var(--primary); }

& so on.

It should be clear here that editing styles written in a utility-based framework really consists of editing the markup itself, rather than messing with the stylesheets. Which solves the change-some-code-mess-up-styling-elsewhere problem.

But I like my markup to be more semantic

There's an old saw that I like to trudge out in conversations like this about how "code gets written once, but read over and over." The basic idea is that the ergonomics of writing code are a lot less important than the ergonomics of coming back to that code and understanding what the heck is going on. All the velocity in the world isn't going to save you when you come back to your templates after a year of, say, being burnt out on a global pandemic and you have no idea what anything is.

This is one of the great things about component-based application structures: everything's got a nice label on it! So even if you're using a utility-only CSS framework, instead of slinging these around...

<button type="button" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 border border-blue-700 rounded">
 Read more
</button>

...you package it all into a component and render it like this:

import Button from '../components/Button';

const SomethingUsingAButton = () => <Button>Read more</Button>;

Unfortunately, component-based systems aren't quite the norm. Sometimes I work on WordPress websites. Or I'll inherit an application written in Express and Jade (or Pug? or Jade?). Recently I’ve been working on a website where templates are assembled in Go. Or maybe I am using Vue, but it's a side project and I want to just get something up on the internet and I don’t want to build a whole library of components for something that'll end up being a single page anyway.

In those cases, returning to a project after a while and finding hundreds of CSS classes—and no semantic affordances anywhere—stops me dead in my tracks before I've even started.

Hybrid BEM + utilities

BEM is probably the opposite approach to using CSS utilities. And it's nice because it forces you to scope your CSS to your components, even if you’re not building using a component-based framework. It gives you a bit of the flexibility & reusability of a component-based approach—at least from a styling perspective. And I think that it also encourages you to consider your markup’s design as you build—if only to prevent selectors like .ListCard--selected__inner__left.

Not only that, but properly written, it's eminently readable. You don't have to go scanning around to figure out what .BlogPost__meta is: it's the meta inside of a BlogPost.

The scoping also means that your editing a style in one place isn’t going to start messing with components elsewhere. Every component focuses exclusively on its own styles. Changing the .BlogPost__meta style isn't going to inadvertently change some padding in your .NavItem__link, for example.

How I use utilities

There are some obvious use-cases for CSS utilities. Anywhere you need some sort of consistency across your site, a utility will probably come in handy. Some examples include .text-white or .text-primaryfor text colours, .text-left or .font-bold for text styles, and .stack or .grid for basic layouts.

One thing that most utility libraries will do is give you some sort of syntax for handling media queries and pseudoclasses. Tailwind has hover:bg-blue-400 and md:flex, for example. I'm not a huge fan of these because I write my own utilities and these require a ton of extra boilerplate.

I also generally use utilities to build small, reusable components that I don't think merit their own capital-C component in whichever framework I'm using. The two that spring to mind are .button, which should be pretty self-explanatory, and .media, which wraps img and video elements to set aspect-ratios or backgrounds. Planning on going more into detail on these in the future.

How I use BEM

For my BEM blocks, I always use PascalCase. When I'm using components to build my markup, I try to make the blocks match up with the components. That way, if you come across a .BlogPost in view-source or something, you know where to look in your filesystem. The whole point of using BEM here is to increase readability.

What this looks like in practice

Here's an element that displays an image on one side and some text on the other, with a dark-coloured background, that we built for a client of ours recently.

<div class="SplitBlock SplitBlock--dark section section--dark">
 <div class="container">
 <div class="SplitBlock__inner">
 <div class="SplitBlock__content stack">
 <h2>Section title</h2>
 <p>Lorem ipsum</p>
 </div>

 <div class="SplitBlock__imageWrap">
 <div class="SplitBlock__image">
 <div class="media media--full rellaxWrap">
 <img
 srcset="https://placehold.it/650x650 650w,
 https://placehold.it/1280x720 1280w,
 https://placehold.it/1980x1280 1920w"
 sizes="(max-width: 450px) 650px,
 (max-width: 1280px) 1280px,
 1920px"
 src="https://placehold.it/1920x1280"
 alt="Section title">
 </div>
 </div>
 </div>
 </div>
 </div>
</div>

Note the different types of classes being used here: .section to add some basic section styles, .container for containing the text on the page, .media for wrapping images; but .SplitBlock__imageWrap and .SplitBlock__content for applying SplitBlock-specific styles.

In concl.

I've been using this method of writing CSS for a few years now and I've settled into a good groove with it. It strikes a great balance between the flexibility of utility-based approaches and the structure of BEM styles.

Code

Next

The good, the bad, & the confusing JavaScript

My entry in the canon of explanation for why JS developers always seem so tired

Previous

The Longest Day