Understanding BEM

5 minute read

This post was originally written for the enotes.com engineering blog.

Understanding BEM

BEM (Block Element Modifier) is a front-end methodology, created to help build succinct and better organized CSS code.

Let’s look an example of code that uses regular class names, and compare it to code that uses BEM.

<!-- non BEM -->
<form class="search" action="/">
  <label for="search-box" class="label required">Search</label>
  <input class="input" id="search-box" type="search" placeholder="What are you looking for?">
  <input class="button icon" type="submit" value="Search">
</form>
<!-- BEM -->
<form class="search" action="/">
  <label for="search-box" class="search__label search__label--required">Search</label>
  <input class="search__input" id="search-box" type="search" placeholder="What are you looking for?">
  <input class="search__button search__button--icon" type="submit" value="Search">
</form>

Although the BEM markup looks similar, and perhaps a little longer with the class names, the advantages become more obvious when we compare the CSS for each example.

/* non BEM */

.search {
  margin: 0;
  padding: 0;
  ...
}

/* specificity 0.0.2.0 */
.search .label {
  font-size: 14px;
  color: #CCC;
  ...
}

/* specificity 0.0.3.0 */
.search .label.required {
  color: #FF0000;
  ...
}

/* specificity 0.0.2.0 */
.search .input {
  padding: 5px 10px;
  font-size: 18px;
  ...
}
/* specificity 0.0.2.0 */
.search .button {
  font-size: 18px;
  ...
}

/* BEM */

/* specificity 0.0.1.0 */
.search {
  margin: 0;
  padding: 0;
  ...
}

/* specificity 0.0.1.0 */
.search__label {
  font-size: 14px;
  color: #CCC;
  ...
}

/* specificity 0.0.1.0 */
.search__label--required {
  color: #FF0000;
  ...
}

/* specificity 0.0.1.0 */
.search__input {
  padding: 5px 10px;
  font-size: 18px;
  ...
}
/* specificity 0.0.1.0 */
.search__button {
  font-size: 18px;
  ...
}

As you can see, we’ve removed complexity, and reduced the specificity of our selectors, by rewriting our descendant selectors as simple selectors. This speeds up the time it takes for the browser to locate all of the targeted elements on the document.

Now let’s look at the different parts of BEM.

Block

A block is a self-contained entity on the page. Keep in mind that blocks can be compound, in which blocks have other blocks in them. (e.g: A navigation block inside of a header block.)

Say we want to create a <figure> which displays an image on the page. We create the .avatar block and apply that class to the html element that represents that block.

<figure>
  <div class="avatar">
    <img src="https://www.enotes.com/images/logos/logo-icon.jpg" alt="eNotes.com Logo">
  </div>
</figure>
.avatar {
    margin: 0;
    padding: 0;
}

Element

An element is an individual piece of a block. They make no sense on their own, and are tied to a block. In the following example, we add an img element to the .avatar block. We call it .avatar__image, and apply that class to the img. This reduces the specificity of the selector, and allows us to target the element on the page a lot faster.

<figure>
  <div class="avatar">
    <img class="avatar__image" src="https://www.enotes.com/images/logos/logo-icon.jpg" alt="eNotes.com Logo">
  </div>
</figure>
.avatar {
    margin: 0;
    padding: 0;
}

.avatar__image {
    width: 50px;
    height: 50px;
}

Modifier

A modifier changes or describes a block or an element. In the following example, we want the .avatar__image element to display a rounded image, by applying a border-radius: 50% to that element. We create the .avatar__image--rounded modifier class, and apply it to the img element. Now, we can tell right away that we are dealing with a rounded image.

<figure>
  <div class="avatar">
    <img class="avatar__image avatar__image--rounded" src="https://www.enotes.com/images/logos/logo-icon.jpg" alt="eNotes.com Logo">
  </div>
</figure>
.avatar {
    margin: 0;
    padding: 0;
}

.avatar__image {
    width: 50px;
    height: 50px;
}

.avatar__image--rounded {
    border-radius: 50%;
}

That is pretty much the gist of BEM. Now, let’s look at a few recommendations.

Recommendations

The following are some helpful recommendations to solve some common issues I ran into while learning about BEM.

Do not use html elements as selectors

BEM can sort of provide us with a scope by using unique class names. If you start using HTML elements in your selectors, you’ll be missing out on one of the benefits of BEM. Not only that but you can also fall prey to directly targeted elements, in which directly targeted elements will always take precedence over inherited styles.

Applying Modifier

You have to apply both the element class and the modifier class to the HTML element. Since the modifier class has the same specificity as the element class, we avoid inheritance conflicts.

<ul class="menu">
  <li class="menu__item">Item 1</li>
  <li class="menu__item menu__item--gear">Item 2</li> <!-- correct -->
  <li class="menu__item--gear">Item 3</li> <!-- incorrect -->
</ul>

Nested Components

BEM is not meant to communicate document structure. When you are building the BEM tree, think in terms of composable components.

Block__Element__Element

If you find yourself writing classes that end up looking like .edit-account-profile__form__label, this should indicate to you that you may have to rethink your BEM tree.

<div "edit-account-profile">
  <form class="edit-account-profile__form">
    <label class="edit-account-profile__form__label" for="account-name">Name</label> <!-- element element 😕 -->
    <input id="account-name" class="edit-account-profile__form__input" type="text" name="name" placeholder="What does your mamma call you?">
    <input class="edit-account-profile__form__save" type="submit" name="save" value="Save">
  </form>
</div>

To solve this use the block in which the element is a part of as your selector (e.g. .edit-account-profile__label.)

<div "edit-account-profile">
  <form class="edit-account-profile__form">
    <label class="edit-account-profile__label" for="account-name">Name</label>
    <input id="account-name" class="edit-account-profile__input" type="text" name="name" placeholder="What does your mamma call you?">
    <input class="edit-account-profile__save" type="submit" name="save" value="Save">
  </form>
</div>

Element to Block

Sometimes an element is really a self contained entity. Since a block can be compounded, you can alleviate issues with nested components, by changing the element into a new block that composes the parent block.

<ul class="menu">
  <li class="menu__item">Item 1</li>
  <li class="menu__item">Item 2</li>
  <li class="menu__item">
    <!-- search block composes menu block -->
    <form class="search" action="/">
      <label for="search-box" class="search__label">Search</label>
      <input class="search__input" id="search-box" type="search" placeholder="What are you looking for?">
      <input class="search__button" type="submit" value="Search">
    </form>
  </li>
</ul>

Conclusion

BEM helps us solve a lot of issues related to specificity and css structure. It helps avoid inheritance issues, and reduces conflicts in long living projects. It may take a little getting used to at first, but, it will pay you dividends when you start moving entities on the page. You and your team members will be able to quickly ascertain how entities on the page are related, and add to them with little to no friction.

Acknowledgements

Thanks to @nicholascloud, @mfonda, and @meganos for reading a draft of this article.

Resources