Skip to main content
Category

Code

Introduction to Block Element Modifier (BEM)

By Code No Comments

What is BEM?

Block Element Modifier (BEM) is a way to write CSS that makes it reusable, predictable, and manageable especially for large projects. Just like how we have Object-Oriented Programming (OOP) for PHP and Functional Programming (FP) for JavaScript, BEM helps organise CSS code so that it’s more manageable.

How BEM Works / Why Use BEM

With CSS, it’s really easy to create difficult to manage code. BEM was created to solve these problems if the entire team writes CSS code a certain way.

CSS global scope

CSS isn’t an OOP language and hence doesn’t have private instance variables. Even with CSS custom properties / CSS variables, we don’t have the encapsulation / restricted scope that OOP private instance variables have. In other words, CSS rules are global and 1 rule in a stylesheet can interact with any other rule in the same or even different stylesheets.

Without the encapsulation / restricted scope, some CSS that you wrote for a footer can easily interact badly with some other CSS that you wrote for the navbar if both contain menu elements.

CSS can become messy and unmanageable

This is perhaps the biggest reason why CSS is so easily messed up. It’s so difficult to check if our code affects other code once our code base gets large.

BEM restricts CSS specificity

In order to make CSS more manageable, BEM essentially restricts how CSS is written by styling (mostly) classes.

To understand why this works, we need to learn more about how CSS works. So go over to MDN’s explanation of CSS specificity and take 5 min to understand what it does. Or you can read this simple introduction to CSS specificity too.

CSS specificity calculation visualized
CSS specificity calculator: https://specificity.keegan.st/

Done? Now you should know that there’s 6 different levels of priority that CSS rules are calculated. In order from most important to least, these are:

  1. !important
  2. Inline styles <span style="display: block;"></span>
  3. IDs <span id="top"></span>
  4. Classes .button, attributes [type=text], pseudo-classes, e.g. :hover
  5. Elements div, a, p, pseudo-elements :before, :after
  6. Order of CSS rules, bottom overrides top

What BEM does is avoid to turn the list above into the list below:

  1. !important
  2. Inline styles <span style="display: block;"></span>
  3. IDs <span id="top"></span>
  4. Classes .button, attributes [type=text], pseudo-classes, e.g. :hover
  5. Elements div, a, p, pseudo-elements :before, :after
  6. Order of CSS rules, bottom overrides top

By essentially styling only classes, BEM almost guarantees that all CSS rules have the same specificity. Hence the only thing that matters is where the CSS rules is placed – bottom overrides top.

1. No !important

2. No inline styles

3. No styling of IDs

4. No styling of elements

5. Minimal nesting e.g. .parent .child

By restricting ourselves to use style (mostly) class selectors, and with minimal nesting, we are almost guaranteed that all the CSS code we write will work in a predictable manner.

But if we have only classes in our stylesheets, we will need a way to manage them better. Enter naming conventions.

Naming conventions

BEM classes are written in a special manner that attaches meaning to underscores and dashes in CSS classes. From what I’ve read, there’s mainly 2 ways of writing BEM CSS:

This is probably what you’ll find when you Google BEM. CSS code written this way use double underscores and double dashes extensively in CSS classes, like this: block__element--modifier.

And if we have Blocks, Elements, or Modifiers that have > 1 word in their names, we use a single dash to separate the words. Like this: block-name__element-name--modifier-name.

Some examples:

  • Block: button
  • Element: button__text
  • Element: button__icon
  • Modifier on element: button__icon--left
  • Modifier on element: button__text--bounce
  • Modifier on block: button--success
  • Modifier on block: button--disabled
  • Block: login-form
  • Element: login-form__radio-input
  • Element: login-form__submit-button
  • Modifier on element: login-form__radio-input--error
  • Modifier on element: login-form__submit-button--disabled
  • Modifier on block: login-form--active

Russian / Yandex Way

Since Yandex essentially gave birth to BEM, this should be recognised as the original doctrine. There’s not difference in the way double underscores have special meaning to separate Blocks from Elements in CSS classes e.g. block__element. However the difference lies in Modifiers – there’s no double dashes -- before a Modifier. Instead the modifier is separated from the Block / Element via a single underscore _.

In addition, there’s 2 kinds of Modifiers: boolean and key-value.

A boolean Modifier just specifies whether the Modifier is present or not, i.e. a boolean Modifier has only true / false values. For example, a button can be disabled or not, leading to button_disabled. A button can also be active or not, leading to button-active.

A key-value Modifier instead has multiple values for the Modifier. Prime examples include color, where the button can be green or red or blue etc. For key-value Modifiers, a single underscore is still used to separate the Block / Element from the Modifier. However, there’s an addition single underscore that separates the Modifier name from the Modifier value, leading to classes like button_color_green, button_color_blue etc.

More examples:

  • Block: button
  • Element: button__text
  • Element: button__icon
  • Key-value Modifier on element: button__icon_position_left
  • Key-value Modifier on element: button__text_animation_bounce
  • Key-value Modifier on block: button_validation_success
  • Boolean Modifier on block: button_disabled
  • Block: login-form
  • Element: login-form__radio-input
  • Element: login-form__submit-button
  • Key-value Modifier on element: login-form__radio-input_validation_error
  • Boolean Modifier on element: login-form__submit-button_disabled
  • Boolean Modifier on block: login-form_active

Which to follow?

The popular naming convention seems to be more readable as double underscore and double dashes are more visibly distinct compared to double underscores and single underscores. However, the Yandex method has is more semantic and explains what the Modifiers are. Personally, I’d choose the Yandex method as I can more easily understand what the Modifiers mean.

Regardless of how you write the classes, you need to agree within your team what naming convention to follow and be consistent. Maybe even use a post css BEM linter plugin to highlight inconsistencies.

What is a Block?

A functionally independent page component that can be reused. In HTML, blocks are represented by the classattribute.

https://en.bem.info/methodology/quick-start/#block

A Block is similar to a Lego brick. There are different kinds of Blocks just like there are different kinds of Lego bricks e.g. square, rectangle, circle, floor, grass, tree, wheel, head, hair etc etc. Similar to how a castle is made out of different Lego bricks, we can make a website out of different Blocks including the header, menu, blog-list, blog-post, filters, footer, and others.

If we follow the BEM methodology in writing Blocks, we can be assured that these Blocks will be reusable and look the same way across pages and projects.

Cute image of lego people inside a keyboard
Photo by James Pond on Unsplash

What is an Element?

A composite part of a block that can’t be used separately from it.

https://en.bem.info/methodology/quick-start/#element

While I disagree that an Element can’t be used separately from a block, it’s true that a Block can be made up of one or more Elements. Take for example a button with an icon in it. In this kind of button Block, we have a button__text Element and a button__icon Element.

Buttons with text and icons as elements

That being said, it doesn’t make sense to use Elements by just placing them outside a Block, i.e. don’t use button_icon if it’s not within a button Block parent. I know sometimes we want to do that because we want to make it look different from a default icon, but the BEM way to do that is to use Modifier(s) instead.

No Elements of Elements

One common mistake that I’ve made earlier in my coding life is to use double underscores more than once in a class, e.g. login-form__input-group__input and login-form__input-group__icon. Maybe something like this:

<div class="login-form">
    <div class="login-form__input-group">
        <input type="email" class="login-form__input-group__input">
        <i class="fa fa-envelope login-form__input-group__icon"></i>
    </div>
    <button class="login-form__button">Submit</button>
</div>

However, having elements of elements / sub-elements like that makes the class names really long. Instead, we could just make them elements only, like this:

<div class="login-form">
    <div class="login-form__input-group">
        <input type="email" class="login-form__input">
        <i class="fa fa-envelope login-form__icon"></i>
    </div>
    <button class="login-form__button">Submit</button>
</div>

Or if need be, and maybe even better, separate into 2 Blocks – login-form and input-group one containing the other, like the below, where the login-form acts more like a box where we place the input-group Block:

<div class="login-form">
    <div class="login-form__inputs input-group">
        <input type="email" class="input-group__input">
        <i class="fa fa-envelope input-group__icon"></i>
    </div>
    <button class="login-form__button">Submit</button>
</div>

What is a Modifier?

An entity that defines the appearance, state, or behavior of a block or element.

https://en.bem.info/methodology/quick-start/#modifier

Modifiers, as the term suggests, modifies the block or the element it is applied to. Per the Yandex naming convention, we can have 2 types of Modifiers: boolean and key-value. In fact, just read the above section on Yandex to understand more.

Close Menu

Remember to get up and stretch once in a while