An Introduction to BEM, How It Works, and Why It's Awesome

Zach Zach (248)
0

Naming things is hard.

"There are only two hard problems in Computer Science: cache invalidation and naming things." — Phil Karlton

Truer words were never spoken. Naming things while you're coding is a pain in the ass. Not only do you have to think about what you're coding a web component for, but the increased demand for component modularity dictates that you must think of every eventuality that said component might be used for in the future.

Posted in these interests:
h/css4 guides
h/code69 guides
h/webdev60 guides

Enter BEM

BEM, which stands for Block Element Modifier, provides a modular, low-specificity approach to structuring your HTML and CSS. BEM is a methodology for naming and structuring your HTML and CSS in a modular way.

In a nutshell, every HTML component (or "module") you code consists of a block, element(s), and modifier(s). Let's break it down:

Block

A block is a a standalone entity that is meaningful on its own. For example, let's say you have a calculator. The calculator itself is a block since it stands on its own and is the primary "entity".

Blocks are denoted by normal class naming, with multiple words delimited by a single hyphen (e.g. .progress-bar):

<div class="calculator">
    Calculator content..
</div>
.calculator {} // block

Element

An element is a component of a block. An element cannot stand on its own. For example, a <div> containing a list of calculator definitions is an element.

Elements are denoted by a double-underscore (__):

<div class="calculator">
    Calculator content..
    <div class="calculator__definitions">
        Some definitions..
    </div>
</div>
.calculator {} // block
.calculator__definitions {} // element

Modifier

A modifier represents an alternative version or state of a block or element. Put another way, modifiers are not "things", but "characteristics" of a thing.

There are two basic types of modifiers (block modifier and element modifier), and both are denoted by a double-hyphen (--):

<div class="calculator calculator--alternate">
    Calculator content..
    <div class="calculator__definitions calculator__definitions--expanded">
        Some definitions..
    </div>
</div>
.calculator {} // block
.calculator--alternate {} // block modifier
.calculator__definitions {} // element
.calculator__definitions--expanded {} // element modifier

As you can see, modifiers can be applied to either blocks or elements, and usually represent states (e.g. expanded) or variations (e.g. alternate) of the block or element.

BEM Specificity and Nesting

One huge strength of BEM is that it utilizes low-specificity while maintaining modularity and avoiding class name collisions. What do I mean by "low specificity"? This means that the number of nested classes remains low. This is important because lower specificity means faster CSS rendering and, since CSS is render-blocking, faster page rendering.

Here's an example of BEM and non-BEM class naming that illustrates levels of CSS specificity:

Example Levels of Specificity Specificity
.dog 1 Low
.dog .dog-tail 2 Low
.dog .dog-tail .dog-tail-tip 3 Medium
.dog .dog-tail .dog-tail-tip .dog-tail-hair 4 High

Generally speaking, you never want to nest your CSS more than 3 levels deep if you can avoid it — but it's even better if you can keep that number as low as possible. BEM allows you to nest your classes 1 level deep.

BEM and SASS

SASS (syntactically awesome stylesheets, but utilizing the SCSS format) is amazing, but can quickly lead to many levels of nesting.

BEM allows us to use the power of SASS, like nesting our SASS to keep it clean and organized, without creating these additional levels. For example:

SASS (SCSS):

.calculator {
    background: #fff;
    &--alternate {
        background: #eee;
    }
    &__definitions {
        display: none;
        &--shown {
            display: block;
        }
    }
}

Output (CSS):

.calculator {
    background: #fff;
}
.calculator--alternate {
    background: #eee;
}
.calculator__definitions {
    display: none;
}
.calculator__definitions--shown {
    display: block;
}

Thus, we have clean SASS, low-specificity (1 level), and a low chance of conflicts with other modules on the page.

Good and Bad BEM Practices

Now that you have the naming conventions down, let's go over some good and bad BEM practices:

Nesting

As stated above, always go for low-specificity. BEM entities themselves do not need to be nested.

Good:

.calculator__definitions {}

Bad:

.calculator .calculator__definitions {}

Dat low-specificity.

Not Every HTML Entity Needs BEM

Of course, not every HTML tag on your page needs BEM naming -- so nesting is still great for non-BEM entities:

.calculator__definitions dt {}

It would be silly to add a class to every <dt> tag in your calculator definitions list just for the heck of it. Ideally, dt would be styled globally or its parent, <dl>, might be its own block if definitions are used elsewhere on your site (outside of calculator-definitions):

<dl class="definitions-list">
    <dt class="definitions-list__term">Term</dt>
    <dd class="definitions-list__definition">Definition</dd>
</dl>

Note: Personally, I would never add classes to tags in this manner and would instead style them globally — unless I'm running, for example, a dictionary website where I use the definition HTML tags in numerous places and with completely different styling. Either way, BEM gives you a lot of flexibility in making those decisions.

BEM Ordering

This one seems obvious as you start working with BEM, but elements are not applied to modifiers.

Good:

.calculator__definitions--shown {}

Bad:

.calculator__definitions--shown__something-else {}

Conclusion

In conclusion, BEM is awesome. I've been writing CSS a certain way for over ten years and in under a day I've nearly mastered it and already see its true value. It seems weird at first to change conventions like these, but it's totally worth it and is a huge time-saver in the long-run.

I highly recommend it when building a website that utilizes modular web components or when working on a site frequently modified by multiple developers.

Questions? Comments?

Post them below! Also, be sure to check out the official BEM website.

Dayne Dayne (57)
0

Event delegation is one of the many concepts that makes JavaScript unique.