BEM is an acronym for block, element, modifier. So what does that mean? BEM methodology was first introduced by Yandex in 2010. Yandex are a Russian technology company similar to Google that provide search engine, mail, market, and money services in Russia. The BEM methodology was introduced by the company to provide a more structured and maintainable approach to writing CSS.
So how does it work?
In component driven design we treat each component as a "block" and within the "block" are "elements" and "elements" may differ from one another through the use of "modifiers". For example let's take a common card design pattern and write some bog standard html with traditional CSS classes.
<article class="card">
<img src="/css-bem.jpg" alt="A frustrated CSS developer with a bowl of CSS BEM soup.">
<span class="date">8 Jul 2024</span>
<h3 class="title">CSS BEM vs ABEM vs BBEM</h3>
<p class="copy">Are you a BEM, ABEM, or BBEM person?</p>
<div class="tags">
<span>#CSS</span><span>#BEM</span>
</div>
<a href="/blog/css-bem-abem-bbem">Read the article</a>
</article>
BEM CSS styling would end up looking something like this:
.card {
background-color: #eee;
padding: 20px;
}
.card img {
border-radius: 5px;
display: block;
width: 100%;
}
.card .date {
font-size: 0.8rem;
}
.card .tags {
display: flex;
gap: 5px;
}
.card .tags span {
background-color: white;
border-radius: 5px;
display: block;
padding: 2px 4px;
}
The problem with this is that we've got varying degrees of specificity going on and overriding any of these patterns later will require some studying of the code and inevitable frustration when trying to figure out what level of specificity is needed to override the styles already written.
BEM CSS methodology gives us a way to equalise the specificity of absolutely every single style written by applying a class to absolutely everything and then only ever targeting a single class in a stylesheet. We'll explore that in just a moment but first I'll show you how it works.
If our CSS classes are to be made up of block, element, and modifier we'll need a convention in order to make it clear what is happening in both the HTML and CSS. Here are the basic rules of BEM.
- Each component of a BEM class must use kebab-case. This means all letters are lowercase and joined by a
-
character. - The block component and element component of a BEM class will be concatenated with a double underscore
__
. - The element component and any optional modifier component of a BEM class will be concatenated with a double dash
--
.
So what would this look like for our above example?
<article class="card">
<img class="card__img" src="/css-bem.jpg" alt="A frustrated CSS developer with a bowl of CSS BEM soup.">
<span class="card__date">8 Jul 2024</span>
<h3 class="card__title">CSS BEM vs ABEM vs BBEM</h3>
<p class="card__copy">It's the battle of the BEMs. BEM, ABEM, and BBEM go head to head!</p>
<div class="card__tags">
<span class="card__tag">#CSS</span>
<span class="card__tag">#BEM</span>
</div>
<a class="card__link" href="/blog/css-bem-abem-bbem">Read the article</a>
</article>
Styling this would end up looking something like this:
.card {
background-color: #eee;
padding: 20px;
}
.card__img {
border-radius: 5px;
display: block;
width: 100%;
}
.card__date {
font-size: 0.8rem;
}
.card__tags {
display: flex;
gap: 5px;
}
.card__tag {
background-color: white;
border-radius: 5px;
display: block;
padding: 2px 4px;
}
Notice that now every CSS rule is equal specificity make it easier to read, easier to manage, and quicker to run.
I've not shown how modifiers work in the example above so let's show that with a different type of component. Let's imagine a call to action banner in the next example.
<div class="cta-banner">
<a class="cta-banner__link cta-banner__link--button-link cta-banner__link--button-link-primary" href="/buy">Buy now</a>
<a class="cta-banner__link cta-banner__link--button-link" href="/buy">Book a demo</a>
<a class="cta-banner__link" href="/buy">Learn more</a>
</div>
We want our first link to look like a button and be "primary" to call attention to itself. We want our second link to look like a button but not be as prominent or "primary" as the first link. And the third link we want to appear as a regular text link as it is our tertiary call to action. Let's style it!
.cta-banner {
align-items: center;
background-color: #eee;
display: flex;
flex-wrap: wrap;
padding: 40px;
justify-content: center;
}
.cta-banner__link {
color: steelblue;
display: block;
text-decoration: underline;
}
.cta-banner__link--button-link {
border: 2px solid steelblue;
padding: 5px 10px;
text-decoration: none;
}
.cta-banner__link--button-link-primary {
background-color: steelblue;
color: black;
}
Again, CSS specificity is equal among all the BEM selectors but allows us to use CSS inheritance through repeated classes to keep the CSS code lean and efficient.
But if you're like me you'll probably be looking at the sheer amount of bloat in that HTML and silently crying inside. And that's where ABEM comes to the rescue.