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.
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.
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:
!important
- Inline styles
<span style="display: block;"></span>
- IDs
<span id="top"></span>
- Classes
.button
, attributes[type=text]
, pseudo-classes, e.g.:hover
- Elements
div, a, p
, pseudo-elements:before, :after
- Order of CSS rules, bottom overrides top
What BEM does is avoid to turn the list above into the list below:
!importantInline styles<span style="display: block;"></span>IDs<span id="top"></span>- Classes
.button
, attributes[type=text]
, pseudo-classes, e.g.:hover
Elements
, pseudo-elementsdiv, a, p:before, :after
- 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:
US / Popular Way
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
https://en.bem.info/methodology/quick-start/#blockclass
attribute.
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.
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.
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.
Recent Comments