DEV Community

Zachary Levine
Zachary Levine

Posted on

BEM Best Practices

Over the course of several projects and team structures, I have tried a few different CSS naming and organization schemes, but in the end I have settled (so far!) on BEM. While there are many great articles that explain BEM’s basic principles and why they are helpful, I feel like they do not spend enough time discussing some of the practices that developers can use alongside BEM to help it truly shine. In this article I will introduce BEM but leave a lot of the introduction to the BEM website itself. From there, I will go through the set of mostly formalized practices that I and my current work team have come to love.

The benefits of BEM

First off, BEM has a great website for introducing people to the BEM method of CSS class naming, and I highly suggest it.

BEM is an approach to naming your CSS classes that I find provides fantastic semantic meaning and context given the resulting class names that appear in what might be otherwise plain and semantics-lacking HTML. When sticking (whenever possible) with CSS classes instead of other selectors, it provides a powerful organizational approach to your CSS by implicitly encouraging a one-to-one relationship between HTML elements and CSS classes.

What BEM is

It does the above by enforcing a naming scheme based on Blocks, Elements, and Modifiers (hence BEM). Going forward, anytime one of these words is used in the context of BEM it will be capitalized (e.g. "Block" instead of "block").

The "Block" is supposed to represent an entity in your HTML that can stand on its own. An example of such would be a particular HTML element containing child elements, or more explicitly the root HTML element of a component. Classes for Block elements are simply the name you give the block.

"Elements" are the child elements of a Block that have no real meaning outside of the Block they are contained within. CSS classes for Elements are named with the following format: Block__Element (note the double underscores). In other words, in a Block called "header" and an Element called "title" we would end up with header__title.

This is the core of BEM right there! Here is a small code example.

<div class=”header”>
  <span class=”header__title”>My Title</span>

As you can see just by looking at the class names, the div is the Block and the span is the Element. While the double underscores come across as ugly to some, their use provides instant recognition of what Block the span is related to. While HTML5 provides some semantic elements and components obviously have their own name as an identifier with semantic meaning, this adds a much richer selection of names as they allow the developer to create names with semantic meaning based on the context of what they are creating. This is not to say that you can just use BEM and avoid HTML5’s semantic tags - I strongly advise using them along with BEM for greater context (and better accessibility).

Before moving on, let’s discuss the Modifier. The Modifier is a class for when you need to override or, get this, modify the normal Block or Element’s CSS styles. They are signified with a double dash between the class they override and the Modifier’s name. A couple examples would be

  1. user--current-user to change the appearance of a user card, such as when the user referenced is the current user

  2. Or, navbar__navbar-item--current-route to highlight the user’s current route within a list of navbar items (e.g. navigation links).

Note the use of the single dash within navbar-item. There is no reason why you can’t use single dashes in each part of a BEM class name. The double dash and underscore are specifically to allow normal CSS class names such as navbar-item for each defined Block, Element or Modifier.

Some basic best practices

There are a few different approaches to BEM, but there are a few practices that are consistent across all of the ones I have seen, as follows. Note that they are not specific to BEM, but combined with BEM provide for a much more seamless and developer-friendly experience.

First, always try to use classes and not other selectors. Second, always keep to one BEM-named class per HTML element. The one exception to this rule is when applying Modifiers, as they are often applied alongside the unmodified class so that the Modifier only needs the overridden or added CSS rules, and no more.

These guidelines ensure, or at least encourage, a naming scheme (even outside of BEM) that will be easy to modify due to consistent and low specificity scores for your CSS classes, and provide a single class that impacts that HTML element. Here are two great articles about CSS specificity for the curious: from the MDN documentation and from Smashing Magazine.

The first point about maintaining low specificity scores means that overriding a class is as easy as adding an overriding class. If this low specificity were not the case, you would have to overcome the fact that while CSS classes only add a low score, the scores are additive. This becomes relevant when using approaches such as nesting your CSS, as that means that classes are combined when they are compiled (meaning, .x > .y becomes .x.y as a single selector). As specificity scores are additive, this means that instead of needing to be more specific than one CSS class, you now need to be more specific than however many levels deep the nested class you are overriding is at. This rapidly becomes unsustainable and painful to override.

The latter point about having a one-to-one mapping of HTML elements to CSS classes is that it results in a much simpler and cleaner styling section in your devtools display, and a single identifier to search your codebase for during development. Further, they make it much easier to identify unused CSS classes as a class that isn’t referred to in HTML is obviously not being used.

More BEM practices I have found useful

On top of all of this are some practices I have picked up and used across a variety of projects that have made this approach even better.

Nesting only for Modifiers. CSS class nesting can be a nice way to avoid rewriting CSS, and while not required for Modifiers to work as expected, it makes for nicer reading to many. It also simply makes sense as Modifiers by their very nature override existing CSS, which is what happens to the more specific nested classes when nesting. Nesting Modifiers also means that they automatically result in The Right CSS, taking what the modified class set and overriding only the necessary values. When using this pattern, you can apply only the Modifier class instead of both it and the overridden class.

However as suggested by the “only”, I highly recommend not nesting Block__Element classes nested underneath their respective Block class. While this can look nice, it means that every Element class will be overriding the Block class’ CSS. This means that any CSS rules the Element class doesn’t care about must also be overridden. This requires that the developer write extra (read as: excess) CSS for overriding what might be app or user agent defaults that the Block set with different values. In my experience this often causes difficulty in refactors or feature changes as the Element class does not come with the expected or globally set defaults. This tends to cause the class to become unnecessarily bloated, and prevents a simple copy-paste of the class when refactoring as these excess CSS rules are not desired in many cases.

Another thing that I do not believe I have seen any articles mention is that your Blocks can be “scoped”! In other words, you can have a Block at the root HTML element of your component, and “declare” a new BEM Block for some child group of HTML elements. I find this greatly increases the semantic value of my BEM classes. The basic principle here is that Block classes declare a new scope within your current BEM context, just like how scope works in Javascript.

I personally do this scoping by taking the Element name from the “BEM scope”(™) I am currently working within and using that (or something with similar meaning) as the new BEM Block scope’s name. Depending on the context I will use the parent scope’s Element with some variation that adds semantic meaning in that context. One way I do this is by taking the Block and Element and hyphenating them to create the new Block. An example of this would be header and header__article becoming header-article as the new Block. This is nice when it isn’t worth it or meaningful to create a new, separate component.

Here is an example on with a “header” and some articles, showing BEM scoping at work. A quick note, in most situations “articles” would obviously be their own component. I am doing it this way here just to present the idea.

Lastly, I address CSS rules that are commonly used together by using “extends”, a feature most CSS pre- and post-processors provide. I use this as it allows a simple importing of a file containing shared classes (which are named with straightforward, non-BEM named styles) and extending the relevant class from that file in the BEM named class that needs those values.

This makes it easy to adhere to the previously mentioned guidelines, such as one class per HTML element, except for Modifiers. This happens while also allowing a simple and composable collection of commonly available utility classes. This works especially well for keeping things visually consistent, such as different input tags used in different components.

Hope this was helpful, and always, suggestions on alternatives or better approaches and practices are appreciated!

Discussion (0)