Using BEM in components

Núria - Apr 29 '20 - - Dev Community

Front-end development has changed a lot in the last 10 years. The most common scenario back then was to have all your site CSS in global styles. On a big site, the number of CSS classes could become unmanageable: it was hard to keep them ordered, you run into class name collision easily, and using !important as a quick fix (that stayed in your codebase forever) was quite common. BEM and similar methodologies (OOCSS, SMACSS, Atomic CSS…) were created to help mitigate those issues. And it worked, if you applied them well.

In case you haven't heard about it, I recommend checking out the documentation. To sum up, BEM is a naming convention for your classes, it stands for Block Element Modifier, and it looks like this:

/* Block */
.card { }

/* Elements */
.card__header { }
.card__button { }

/* Modifiers */
.card__button--disabled { }

Having a strict convention to write class names makes the front-end code more explicit and easier to work with. It also avoids class name collisions, making the code more robust and improving scalability. But of course, using BEM has its drawbacks: class names tend to be very long, which makes your HTML harder to read. For a long time the pros outweighed the cons, but is it still true?

Using BEM today

Nowadays, when styling web components, we can make use of Shadow DOM to make the styles encapsulated in that component, preventing them from affecting outer elements. Similar approaches exist in Angular, Vue or React, either using Shadow DOM as well or appending unique attributes to the class names, to ensure that the styles are scoped to that component.

Since class collision between components is not a problem anymore, a lot of devs consider now BEM obsolete. But I believe that BEM offers more than that, and at Codegram we still use it in our projects.

A component is not a Block

If you check the first sentence in the official site, it says "BEM — Block Element Modifier is a methodology that helps you to create reusable components". But now we already have the reusable component part solved! Imagine a Product component. With the traditional approach, we would think of the product as the Block, and end up with classes like this:

.product {}
.product__header {}
.product__header-title {}
.product__header-subtitle {}
.product__image {}
.product__image-description {}

It's not bad, but is it really necessary to specify in each class that it applies to product? We are already in the product component, and styles will not be applied anywhere else. If a component doesn't necessarily translate to a single Block element, we can use several Blocks inside a single component. We do not need to worry about class collision, so we can keep class names short but meaningful:

.product {}
.header {}
.header__title {}
.header__subtitle {}
.image {}
.image__description {}

Now the responsibility of each class is much clearer just by looking at it, it's easier to split the component later if needed, and overall it's much easier to read.

A convention

You might think that, based on the previous example, you could just skip BEM altogether, and classes would still be short, meaningful and manageable. And you would be right. But still, I find BEM is useful in providing a convention for our class names. In JavaScript (or any language of your choice), it's common to have a convention for the names of the variables. It does not matter which convention you choose, as long as the team agrees on following it. So why should CSS be any different? It's still a language, it's a crucial part of frontend development, and if we forget to apply best practices to it, that's when it becomes cumbersome to work with. Just because the styles don't leak out to all your site anymore, it doesn't mean you should hide all the mess inside your components. So use BEM, or skip it, but make it a conscious decision and put effort and thought when writing CSS.


Cover image by Caleb Angel

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .