The struggles of BEM

Naming CSS classes is hard. The naming convention BEM is suppose to ease the pain, but if you're still struggling chances are you're not using it right.

Before you read on

This is not an introduction to BEM, the methodology or naming conventions in general. This post is written for those of you who've struggled with BEM in your projects, and not quite gotten it right. I'll go through some common pitfalls and try to explain why BEM actually can do wonders if used correctly.


Common pitfalls

BEM stands for Block, Element, Modifier. It doesn't really matter what you call it (I myself view it more as Component, Children, Variants, but 'CCV' does sound as good as 'BEM') as long as you use it as intended.

The intention is well documented, but I've still found myself struggling with the methodology to a point where I started to hate it.

Now, having figured how to avoid most of the pitfalls, I love it, and will probably never go back to not using it.

Here's a few mistakes that I and several others easily makes:

1. Thinking that only direct descendants of a block are elements

It's easy to think that it's only direct descendants of a block can be defined as elements. People often continues the naming convention further down the DOM-tree, thinking that direct descendants of the block are elements written as block__element and grandchildren of the block are sub-elements and should be written as block__element__sub-element.

Like this:

Language: html
<article class="post">
    <div class="post__image-wrapper">
      <img class="post__image-wrapper__image" src="/assets/foo.jpg" />
    </div>
    <div class="post__content">
        <p class="post__content__excerpt">Lorem ipsum dolor sit amet</p>
        <a class="post__content__link" href="/post">Read more</a>
    </div>
</article>

For a long time I thought this was the right way of doing things, making me hate the ugly class names bloating my markup.

However, the documentation clearly states when you should create an element:

"If a section of code can't be used separately without the parent entity (the block)"

This means that grandchildren and other descendants of the block should also be given a name using the block__element naming convention.

Every descendant of a block should be elements and written as such, unless you actually have a block within a block. In that case the naming convention "starts over again" (more on this in point 2).

The code above would be better if it were written like this:

Language: html
<article class="post">
    <div class="post__image-wrapper">
      <img class="post__image" src="/assets/foo.jpg" />
    </div>
    <div class="post__content">
        <p class="post__excerpt">Lorem ipsum dolor sit amet</p>
        <a class="post__link" href="/post">Read more</a>
    </div>
</article>

2. Creating too large blocks

On the other side of the first point it's also easy to overthink the fact that every descendant of a block should be elements, and that only the outermost HTML-tag can be a block.

This leads very big blocks with a lot of elements, making the naming convention hard.

Take a header component like the one below:

Language: html
<body>
  <header class="header">
    <div class="header__inner">
      <a class="header__logo" href="/">
        <img class="header__image" src="/assets/logo.jpg" />
      </a>
      <nav class="header__navigation">
        <ul class="header__list">
          <li class="header__item">
            <a class="header__link" href="/about">About</a>
          </li>
          <li class="header__item">
            <a class="header__link" href="/blog">Blog</a>
          </li>
          <li class="header__item">
            <a class="header__link" href="/contact">Contact</a>
          </li>
        </ul>
      </nav>
    </div>
  </header>
</body>

<header> is the outermost HTML-tag in the tree, a direct descendant of <body>, and given this being a block you'd might think that everything inside must be elements.

This means that naming gets harder the further down the tree you go. Because everything here is an element of the block header it becomes difficult to come up with good names that represents the elements in a good way, like header__item. The name becomes too generic, and there's no way of knowing what it relates too, other than it being a child element of header.

We have to remember what the methodology says about elements:

"If a section of code can't be used separately without the parent entity (the block)"

header__item can't be used separately without header, that is true, but it also cannot be used without header__navigation. Ergo, we can see that header__navigation could (and probably should) be its own block. The same goes for header__logo.

The revised code could be something like this:

Language: html
<body>
  <header class="header">
    <div class="header__inner">
      <a class="logo" href="/">
        <img class="logo__image" src="/assets/logo.jpg" />
      </a>
      <nav class="navigation">
        <ul class="navigation__list">
          <li class="navigation__item">
            <a class="navigation__link" href="/about">About</a>
          </li>
          <li class="navigation__item">
            <a class="navigation__link" href="/blog">Blog</a>
          </li>
          <li class="navigation__item">
            <a class="navigation__link" href="/contact">Contact</a>
          </li>
        </ul>
      </nav>
    </div>
  </header>
</body>

I know that you're thinking: navigation is a generic name, and nothing about it indicates that we're talking about the navigation at the top of our page. That's fine, if you have several navigation components within your site or application (like a list of links in the footer or an anchor menu at the beginning of a blog post) you could name this block differently, like menu, main-navigation, top-nav etc.

The point is that you should, whenever you can, create new blocks, even if they necessarily won't be used separately from other blocks. The navigation in this case will only be used in the header, nowhere else, but it could still be its own block in terms of this naming convention.


3. Replicating the HTML structure in your class names

When I first encountered BEM I believed that the brilliance of it were due to you being able to "see" the structure of your HTML by just looking at the class names.

Stumbling upon this within the CSS files

.btn { ... }
.btn__label { ... }

gave me an indication that the HTML might look something like this:

<button class="btn"><span class="btn__label">Click me</button>

In simple components like this one it might be true, but in larger blocks with deeper nesting of elements it might not.

Therefore I'd often try to name my elements in a way that I also could see the HTML structure, like this:

Language: html
<header class="header">
  <div class="header__inner">
    <a class="header__logo" href="/">
      <img class="header__logo-image" src="/assets/logo.jpg" />
    </a>
    <nav class="header__navigation">
      <ul class="header__navigation-list">
        <li class="header__navigation-list-item">
          <a class="header__navigation-list-item-link" href="/about">About</a>
        </li>
        <li class="header__navigation-list-item">
          <a class="header__navigation-list-item-link" href="/blog">Blog</a>
        </li>
        <li class="header__navigation-list-item">
          <a class="header__navigation-list-item-link" href="/contact">Contact</a>
        </li>
      </ul>
    </nav>
  </div>
</header>

What this gives you is long, complicated class names.

This is not necessary. The most important thing of BEM is not so that you won't have to look at your markup that often, its main strength is about scoping. The hardest thing about CSS is naming classes, understanding the cascade and maintaining code.

Editing a BEM class in your CSS makes you know exactly what blocks or elements that you are editing, making it harder for you to write code that breaks something else.


4. Creating too many modifiers

Modifiers are a great way of creating different versions of components, but it can sometimes become too much.

A classic example of this is a button component. You'll often do something like this:

Language: css
.button { 
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 1rem;
  border-radius: .25rem;
}

.button--primary {
  background-color: blue;
  color: white;
}

.button--secondary {
  background-color: grey;
  color: black;
}

And this is fine, but the problems arise when you start to think that modifiers should cover every possible variation.

"A menu toggle is also a button, so I should create this as a modifier of the .button block".

If you go down this rabbit hole your code ends up like this:

Language: css
.button { 
  padding: 0.5rem 1rem;
  border-radius: .25rem;
}

.button--primary {
  background-color: blue;
  color: white;
}

.button--secondary {
  background-color: gray;
  color: black;
}

.button--menu-toggle {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  border: 0.125rem solid black;
  border-radius: 0;
  font-size: 1.125rem;
  padding: 1rem;
  line-height: 1.25;
  background-color: gray;
  color: darkgray;
}

.button--menu-toogle-open {
  background-color: black;
  color: white;
}

.button--dropdown {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.5rem 1.25rem;
  border-radius: 0;
  box-shadow: 0 .25rem 1rem 0 rgba(0, 0, 0, 0.15);
}

.button--form-submit {
  background-color: gray;
  color: darkgray;
  padding-left: 2rem;
  padding-right: 2rem;
  border-radius: 99em;
}

Most of these modifiers share almost nothing with the original .button. This is where you should stop and think "should this modifier rather be its own block?".

A general rule of thumb is that if a modifier is also in need of a modifier then it should probably be its own block.

A better version might look something like this:

Language: css
.button { 
  padding: 0.5rem 1rem;
  border-radius: .25rem;
}

.button--primary {
  background-color: blue;
  color: white;
}

.button--secondary {
  background-color: gray;
  color: black;
}

.menu-toggle {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  border: 0.125rem solid black;
  border-radius: 0;
  font-size: 1.125rem;
  padding: 1rem;
  line-height: 1.25;
  background-color: gray;
  color: darkgray;
}

.menu-toggle--open {
  background-color: black;
  color: white;
}

.dropdown-toggle {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.5rem 1.25rem;
  border-radius: 0;
  box-shadow: 0 .25rem 1rem 0 rgba(0, 0, 0, 0.15);
}

.submit-button {
  background-color: gray;
  color: darkgray;
  padding-left: 2rem;
  padding-right: 2rem;
  border-radius: 99em;
}

5. Using modifiers as utility classes

It's easy to fall into the trap of creating modifiers that do only one thing and then combine several of them to get the variant you want.

Language: css
.button { ... }

.button--lg-text { font-size: 1.5rem; }

.button--spacious { padding: 1rem 2rem; }

.button--dark { background-color: black; color: white; }

.button--with-border-radius { border-radius: .25rem }
Language: html
<button class="button button--lg-text button--spacious button--dark button--with-border-radius">Click me</button>

The intention of this is good, but it may be a form of over-engineering. It's nice to have, but when do you really need .button--spacious without .button--lg-text? Or a button without .button--with-border-radius? It depends on the design of course. It may be necessary to combine size with color, so there is room for using several modifiers, but not in infinite combinations.

A better approach:

Language: css
.button { border-radius: .25rem }

.button--big { font-size: 1.5rem; padding: 1rem 1.5rem; }

.button--dark { background-color: black; color: white; }
Language: html
<button class="button button--big button--dark">Click me</button>

In conclusion

A classic analogy for CSS development is this Peter Griffin meme:

It's relatable, for sure, but the correct use of BEM in combination with a good CSS architecture (perhaps ITCSS) resolves a lot of the issues that makes CSS feel this way.