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

The old saw about how code is read more often by other humans than by a computer might not quite make sense for HTML.

But I still want to be able to glance at an HTML document and get a sense of what’s what. Where’s the main content, where’s the sidebar? On this blog post, where's the post meta, where's the excerpt? What’s the background-image and what’s the card content? Semantic HTML elements are great but they only get you so far.

The traditional answer to this in modern utility-based CSS frameworks is to abstract away the ‘ugly’ (& it’s not even that ugly) markup by writing custom components. So you can turn this:

<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

...into something like this:

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

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

This is a really nice approach on component-heavy applications, whether you're working in React or Vue, or even modern Laravel.

Buuuuuuuut 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.

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-primary for 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 class="SplitBlock__imageWrap">
                <div class="SplitBlock__image">
                    <div class="media media--full rellaxWrap">
                            srcset=" 650w,
                            sizes="(max-width: 450px) 650px,
                                (max-width: 1280px) 1280px,
                            alt="Section title">

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.