Why using BEM for your CSS is a good idea

Not even those of you with long memories will remember that I wrote a blog post in 2015 about using the Block Element Modifier technique when writing CSS. To save you the trouble, I wrote the following.

…I saw immediately that the concept is based on weak coding principles, not code simplicity and reusability…

Four years and countless projects further down the road, I see from my website statistics that a retraction is due. I’ll say now that I’m not back-pedalling, but have found the application of BEM as a concept much more helpful since learning from Nico‘s experience and in particular through a return to hand-coding without using a pre-defined CSS framework.

My original point stands – that if you have the time and if you aren’t working on a big site, using semantic CSS classes for specific combinations of elements can make sense. But this is rarely the case for a programmer using CSS these days. Sites are much more complex than they were ten or even five years ago, so a little structure can save you a lot of hassle.

By applying a scoping class to an element and its set of constituent elements, you can quickly avoid specificity conflicts, and by using this scope, you will end up writing less CSS. (You’ll still run into specificity conflicts when competing with disorganised CSS from third-party plugins, but that’s a separate issue.) Working with WordPress’ Gutenberg editor this year, I (and my colleagues at work) have achieved an exceptionally lean CSS structure for our projects.

So, how does CSS scoping work? By applying a master class name to the outer HTML element and by adding variations on this master class name to the appropriate subelements.

<article class="c-article">
 <header class="c-article__header">
  <h1 class="c-article__title">Article title</h1>
 <div class="c-article__content">

The generated CSS for this element could look like this. No nesting, no issues with descendant selectors, and rules based purely on simple class names, independently of the actual HTML structure.

.c-article {
  border: 1px solid #eee;
  padding: 1rem;
  background-color: #f0f0f0;

.c-article__title {
  font-size: 2rem;
  margin: 0 0 1rem;

Where did I go wrong back in 2015? Take my price element from that blog post as an example.

<a class="btn" href="/prices/">
 <span class="btn__price">$9.99</span>
 <span class="btn__text">My product</span>

I bemoaned the fact that we might want to apply a common styling to all price elements, stating that the following CSS would probably be necessary.

.btn__price, .list__price, .header__price, .teaser__price {
 font-weight: bold;
 font-size: 110%;

That’s not true: what I overlooked at the time is that I can add multiple classes to the same selector. By using an identifying prefix to the class name (o for objects, c for components), I can combine two to describe each element in a semantic way. Thus, my HTML changes to this.

<a class="c-pricebutton o-button o-button--primary" href="/prices/">
  <span class="o-button__price">$9.99</span>
  <span class="o-button__text">My product</span>

In this instance, I can apply the following styles using SCSS. As you can see, the button rules have simple button styling applied, with a o-button--primary modifier to change the colour styles. The larger size and bold font style comes from the c-pricebutton class name.

.c-pricebutton {
 font-weight: bold;
 font-size: 110%;

.o-button {
 display: inline-block;
 padding: .25em;
 color: blue;
 background-color: white;
 &--primary {
  color: white;
  background-color: blue;

In this example, you could potentially make the same styling change to increase the size and weight of the button using a o-button--large modifier in place of c-pricebutton. However, I feel that this removes a level of semantic definition in favour of a directly visual one, which is inadvisable. Keep the rules for how an element should appear in the CSS, and use purely semantic elements and semantic element class naming in the HTML.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.