Why We are BEMish and not BEM

The point of BEM is so you can namespace your CSS. This is a good thing. However, BEM enocurages over-specificity and discourages code-reuse in the longterm. We did use the original unmodified version of BEM (back when we were writing Angular 1 code) and we found that it actually made our lives harder. So we now use a BEM-inspired structure for our classes and names to encourage reusable CSS with as little nesting as possible to improve rendering performance and minimize the file size.

Let’s take a mid-sized project where you might be building a UI that has lists of things either products, users, categories, etc or for a content site featured articles, most popular, most shared, most recent, etc.

This means for a genric list with a name, image and description you end up with a generic html structure like:

<ul>
    <li>
        <div>
            <img alt="picture of thing">
            <a href="">Name Of Thing</a>
        </div>
        <div>
            <div>Title of thing</div>
            <div>Description of thing</div>
            <div><button>Action 1</button> <button>Action 2</button> <button>Action 2</button></div>
        </div>
    </li>
</ul>

This really generic HMTL now needs classes, so let’s go ahead and add them using BEM.

<ul class="product__list">
    <li class="product__list__item">
        <div class="product__list__column">
        
            <a href="" class="product__list__link"><img alt="picture of thing"  class="product__list__link__thumb"></a>
        </div>
        <div class="product__list__column">
            <div class="product__list__title"><a href="" class="product__list__title__link">Title of thing</a></div>
            <div class="product__list__description">Description of thing</div>
               <div class="product__list__actions">
                   <button class="product__list__actions__action--primary">Action 1</button> 
                   <button class="product__list__actions__action--secondary">Action 2</button> 
                   <button class="product__list__actions__action--secondary">Action 3</button>
               </div>
         </div>
    </li>
</ul>

This is reasonably understandable, it makes it easy to nest with style or scss:

.product
  &__list
    &__item
      &__link
        &__thumb
      &__title
        &__link
      &__description
      &__actions
        &__action
          &--primary
          &--secondary
    &__column

That is nicely structured and nicely nested. But did you notice a problem? The class for the columns, product__list__column should be nested under __item so it should be product__list__item__column. And then everything else should have __column in it too.

Okay, but we split __column out of hierarchy. Why? Because we probably want the left column to be 20% and the right column to be 80% wide. This means we broke our BEM nesting right away and it makes total sense to do so.

So now we have product__list__item__column--narrow and product__list__item__column--wide. So theoretically, this modifier needs to be part of our CSS too: product__list__column--wide__actions__action--primary.

That is neither readable nor usable. It made total sense to not include __column in our class names because it was a modifier for just a wrapper, it had nothing to do with the children directly. But this means we’ve broken our heirarchy once and where you do it once you will do it again and again. This makes things less consistant and each dev will decide when and where to break the hierarch themselves and future devs will have to figure out why random Dev X did it one way when two lines later random Dev Y did it the other way.

But you might say we can use TWO classes: great. Now we have product__list__item__column and product__list__item__column--wide. This seems to work great in this case. But what about other more important modifiers?

But time goes on and we want to feature some of our items: product__list__item--featured and of course some of its children will change their styles.

Ouch, so now we have two choices, we can nest subelements under the modified class

.product
  &__list
    &__item
        &__link
          &__thumb
    &--featured
        &__item
          &__link
            &__thumb

Which results in product__list__item--featured__item__link__thumb or give up our BEM notation and our convienent nesting by escaping and doing things the old fashion way with hierarchy:

.product
  &__list
    &__item
        &__link
          &__thumb
    &--featured
      .product__list__item
        &__item
          &__link
            &__thumb

Now we have a random class stuck in there. We can use stylus to break to the top for us automatically, but the option isn’t much better. So now we have over-specified classes for just our product.

What happens when we add a list of users with basically the same layout?

We can either @extend some generic items__list for both .users__list and .product__list and then modify or we can start a whole new structure without sharing any css. Of course then you lose “DRY”. So now all of our elements need to extend not one but two sets of classes product__list__actions__action--secondary AND generic__list__actions__action--secondary.

This goes on and on and on. Moreover, all of these long and complex classes add a non-trivial amount of filesize. When working with AMP there is a HARD limit on css filesize. For even a moderately complex page with very well separated css, one can find themselves bumping into that limit.

So, our solution:

1. We have blocks, elements and modifiers.

A block is a section of a page. Everything follows from there.

.product
  &__list
  &__item

We can add styles directly to .product but it is prefered not to. It is better to have the styles on a sub element, but it is not always possible for example, if .product is a div element that needs to be full width but .product__list needs to be 80%.

An element is a reusable chunk inside a block. For example, a button or a search form that might appear in multiple blocks.

.btn
  &__wrapper
  &__icon

A modifier always modifies either a block or a element so you can target more narrrowly:

.product
  &__list
    &.m-featured &
.search_form
  &__input
  &.m-home &
    &__input

To ensure modifiers are used correctly, a modifier must always be prefixed with &.m-. It should never be targetted directly: .m-home .search_form--input.

In general, if you think of blocks as sections of a page, then you have very few elements. A block can be nested inside another block.

2. Don’t nest more than parent/child, if you absolutely must MAYBE add an occasional grandchild, but that is not perfered.

.product
  &__list
  &__item
  &__link
  &__thumb
  &__title
  &__description
  &__actions
  &__action

3. Modifiers should be seperate classes and always prefixed with m-.

.product
  &__list
  &__item
   &.m-featured
  &__link
  &__thumb
  &__title
  &__description
  &__actions
  &__action

The simple rule that all m- classes must begin with & ensures that we don’t need to worry about m-featured accidentally being used in multiple places.

4. Mixing modifiers for heirarchy is OK.

.product
  &__list
  &__item
   &.m-featured &
     &__description
  &__link
  &__thumb &
    &__link
  &__title
  &__description
  &__actions
  &__action
   &.m-primary

It magically increases specificity so you don’t even need to worry about order: .product__description{} .product.m-featured .product__description{}.

Because we use few modifiers and only for specific blocks, you rarely have to worry about CSS from some random part of your code interfearing.

5. Use -- not __

We don’t use underscores __ because that’s a lot of shift keys to press and &__something is not any more readable than &--something. Because we use very little nesting and don’t mix in camelCaseStyles like BEM suggests, it’s easier and more readable to just write product--list.

6. Rarely, very very rarely, it is better to target an html element than add classes to all the things.

Sometimes, it’s better to write a form:

.form
  input
  label

Than to add a class that exactly duplicates the clear and obvious heirarchy. But don’t use it too often. There should be a good reason.

.content
  ul
  li
  ol

Styling inside a block of CMS content is the best reason for this rule. Don’t hack your CMS to add classes, just use the html elements. (In fact, we completely strip all classes and styles that any CMS or author adds.)

7. Always lowercase and use - or _ as a separator but PLEASE PICK ONE!

With these rules, we end up with:

<ul class="product--list">
    <li class="product--item">
        <div class="produc--column">
        
            <a href="" class="product--link"><img alt="picture of thing"  class="product--thumb"></a>
        </div>
        <div class="product--column">
            <div class="product--title"><a href="" class="product--link">Title of thing</a></div>
            <div class="product--description">Description of thing</div>
               <div class="product--actions">
                   <button class="product--action m-primary">Action 1</button> 
                   <button class="product--action m-secondary">Action 2</button> 
                   <button class="product--action m-secondary">Action 3</button>
               </div>
         </div>
    </li>
</ul>

And our .styl is structured like:

.product
  &--list
  &--item
   &.m-featured &
     &--description
  &--link
  &--thumb &
    &--link
  &--title
  &--description
  &--actions
  &--action
   &.m-primary
   &.m-secondary

So now, it’s trivial to have our users and our categories list catagories:

.generic
  &--list
  &--item
   &.m-featured &
     &--description
  &--link
  &--thumb &
    &--link
  &--title
  &--description
  &--actions
  &--action
   &.m-primary
   &.m-secondary
   
 &.m-users &
  &-link
  
 &.m-product &
   &--item
     &.m--featured &

So putting it all together for a pretty generic page:

<div class="block header">
    <div class="block--in header--inner">
        <nav class="menu"></div>
        <form class="searchform m-header">
        
        </form>
    </div>
</div>
<div class="block content">
    <div class="block--in content--inner">
      <h2 class="block--title content--title">Title</h2
    </div>
    <div class="content--footer"></div>
</div>
<div class="block contact">
    <h2 class="block--title">Title</h2
    <div class="block--in">
        <form class="content--form"></form>
    </div>
</div>
<div class="footer">
    <div class="footer--inner">
        <form class="searchform m-footer">
        
        </form>
    </div>
</div>

This is 6 seperate blocks: header, menu, searchform, content, contact, footer.

You’ll notice that we have classes .block and .header on the same element. This is usually done so we can share styles across multiple blocks. So the generic .block that might be used on dozens of seperate sections can have generic code setting width, margins etc and the specific .header block can set it’s own code.

The searchform has m-header and m-footer. This for when we have a generic form where the majority of styles is overridden and we want to say a light header and a dark footer and need to change the colors or change the font-size. The .footer does not need to “know” that it contains .searchform. So we modify searchform itself and not do something like .footer .searchform--input{color:red;}.

So for the .content block we see we are reusing block, block--title and block--in. Since we arlready defined .block a set of global styles, we might need to override them. It does not make sense to have .block.m-content for dozens of variations because then your styles are spread all over the place. So like BEM, we declare multiple classes and only override what we need.

You will notice that .content--title and .content--footer completely ignore the hierarchy. This is because we don’t need it and it ensures that block--title and content--title have similiar specificity. You control the heirarchy based on where .block is defined in the css file.

The goal is to have as few overrides as possible.

Finally, for .contact we see that we don’t specify a --inner or --title class. We only define what we need and nothing more.

comments powered by Disqus

Do you want to get in touch?

Let us know what you want to create.

Contact Us