• How we Use BEM to Modularise Our CSS
  • The Nuggets translation Project
  • Translator: Yang Longlong
  • Proofreader: L9m, JasinYip

Modularize your CSS code using BEM

If you’re not familiar with BEM, it’s a way of naming CSS classes in a strict way by dividing them into individual components. It is denoted as Block Element Modifier, and a common BEM looks like this:

.block {}
.block__element {}
.block--modifier {}
.block__element--modifier {}
Copy the code

The principle of BEM is simple: a Block represents an object (a person, a login form, a menu); An Element is a component of a block that functions as a specific function (a help button, a login button, a menu item); A Modifier is how we represent different variations of a block or element (a woman, a mini login box with hidden tabs, a different menu in the footer).

There are enough online resources to explain the BEM approach in more detail (css-tricks.com/bem-101/, getbem.com/naming/). In this article, we will focus on how to address the challenges of applying BEM to your projects.

Before we decided to use the BEM method to transform styles, we did some research. Looking around, we see articles, studies, documents, and more that seem to answer every possible question. Apparently we found our new best friend.

But once you dig a little deeper in one direction, you get confused. The harder you try to make it better, the worse it gets — unless you ignore it and treat it like your friend. Our story began a few months ago, when we met BEM. We went out and introduced ourselves and were seduced by BEM, which we used in some of our toy projects. We were very close, which led to a decision: we liked it and wanted to take our previous friendship to the next level.

The following process is fairly simple and natural. We experimented with naming conventions and manually created style classes. After deciding on a set of guidelines, we created basic mixins to generate class names so that we didn’t have to use a block name every time we added a new modifier or element.

So our journey began like this:

We then converted the above code using a series of custom mixins:

Over time, as more and more edge cases emerge, we change the existing code by adding mixins. Pretty neat!

So if we want to define a list element under the full-size modifier, we need to do this:

How do I use BEM in my program

Instead of converting everything to follow these methods all at once, we smoothly and slowly converted one piece at a time.

Like any rule, we must understand the relationship to get along better. There is no doubt that one of the guiding principles we follow is part of the BEM approach, and there are some rules that we have added later.

The basic rule is that we never nest blocks within blocks and elements within elements. This is a principle we must not break.

Here is a very deep nesting in a block:



If you need more nesting, which means more complexity, you should break the elements into smaller pieces.

Another rule is to convert elements to blocks. Following rule 1, we divide everything into smaller pieces.

Let’s talk about the structure of a related component:

First we create the structure corresponding to the higher-level block:

Then we repeat the smaller internal structure:

If the name becomes more complex, we simply extract it into another, smaller block:

Then add some complexity — we want to add some hover effects:

After all this is done, if we put the code in the stylesheet, it will look well structured:

There is nothing to stop us from cleaning up some unnecessary semantics. Since our part of the code is obviously part of the list and has no other items in the relevant context, we renamed it to photole-item:

Here’s another rule: We use simplified naming to name BEM blocks of nested components so that they don’t conflict with other blocks.

For example, we don’t simplify item-title because we have a photo-title in the main block or preview title. It’s all too common.

Mixins

The mixins we use are part of Paint, an internal style library.

You can find it here: https://github.com/alphasights/paint/blob/develop/globals/functions/_bem.scss

_Paint is a bower/NPM package available, and it is undergoing a core refactoring. BEM mixins are still available and regularly maintained. _d

Why do I need mixins in the first place?

Our goal was to make CSS class generation very simple, because we knew that front-end and back-end engineers didn’t have to spend a lot of time building style sheets. So we automate the process as much as possible.

We also developed a set of helper components that do something similar to templates — provide a way to define blocks, elements, and modifiers, and then automatically generate tag classes just like we do in CSS.

How to work

We have an _bem-selector-to-string function that simply handles the selector, converts it to a string. Sass (Rails) and LibSass (node) do not seem to work together when handling selector strings. Sometimes the dots in the class name are added to the string, so we want to make sure we remove them as a precaution before going any further.

The function we use to check whether a selector has a modifier is _bem-selector-has-modifier. It returns true if modifiers or pseudo-classes (:hover, :first-child etc.) exist.

The last function extracts the block name from a string containing modifiers or pseudo-classes. If all the corresponding block names pass, _bem-get-block-name returns the corresponding block name. When we use internally decorated elements, we need to use block names, otherwise it will be difficult to generate a class name.

Bem-block mixin generates a basic block name with a class name and associated attributes.

Bem – Modifier Mixin generates a. Block name -modifier class name.

Bem-element Mixin does much more. It checks whether the parent selector is a modifier (or a pseudo-class selector). If so, it generates a nested structure containing the block name-the block name of the modifier, and internally containing the block name-element name. If not, we simply create a block name _ element name _.

For elements and modifiers, we currently use @each Elements, but we’ll optimize it in the next version to allow the same attributes to be shared instead of copying attributes in each element.

What enjoyment does BEM bring to us

modular

Adding too much logic to a component is very difficult to refactor. By using BEM, the lack of choice is also a good thing most of the time.

clear

When we look at the DOM, it’s easy to figure out where blocks are, what elements mean, and how modifiers are used. Similarly, when you look at a component style sheet, you can easily find areas where you need to change or add some complexity.

A block structure with interactive components:

A block structure with elements and modifiers.

Team collaboration

Working together on the same style sheet, it is difficult to avoid style conflicts. But by using BEM, each person can work in their own block-element, so it doesn’t affect anyone else.

The principle of

When writing CSS, we like to follow a set of principles/rules. By default, BEM follows the following rules to make writing code for BEM easier.

1. Separation of focus

BEM forces us to style smaller parts, making blocks that include elements and modifiers more maintainable. If the logic becomes too complex, this is the time to break it down into smaller pieces. Rule # 2.

2. Principle of single responsibility

Each block has a single responsibility to encapsulate the contents of the component.

For the initial example, the corresponding section should be responsible for creating a grid of list and preview elements. We do not share internal and external responsibilities.

Following this approach, if the grid changes, we only need to change the content of the corresponding part. The other parts still work fine.

3. DRY (Don’t repeat yourself)

Every time we happen to find code copied, we extract it into placeholders and mixins. If we need to repeat in the current context (important components in the context), use this pattern — define mixins and pseudo-classes with underscores.

Remember not to waste time between used and discarded code and two separate duplicates of code that occasionally have different properties.

4. Open and close principle

This principle is hard to break when using BEM. It states that everything should be open for extension and closed for modification. We avoid changing the attributes of a block directly in the context of another block. Instead we create modifiers to do this.


BEM is a powerful method, but I think the secret is yours to keep. If it doesn’t work sometimes, figure out how it can work and break the rules. As long as it brings structure and productivity, there is absolute value in implementing it.


We’d love to hear that you’re using BEM to solve your big challenges.