BEM CSS... oh my!
BEM. Block. Element. Modifier. A long time naming convention staple for front end developers and component driven design. But what is ABEM? And what is BBEM? If you like drinking acronym soup and BEM CSS doesn't just look like a load of random letters then this one's for you! Let's five in to what is BEM CSS including an exploration of both CSS ABEM and CSS BBEM!
What is BEM CSS?
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.
So what is CSS ABEM?
ABEM naming methodology is similar to BEM CSS but a marked improvement for many reasons. ABEM stands for Atomic Block Element Modifier and was first introduced by Thierry Koblentz in his article titled "Challenging CSS Best Practices" for Smashing Magazine in 2013. Firstly let's discuss the rules of the ABEM naming convention and then dive in to why they're so much better than standard BEM.
- Each component of an ABEM class must use camelCase. This means all letters are lowercase apart from the first letter of any subsequent words in a section.
- The block component and element component of an ABEM class will be concatenated with a double underscore
__
as per BEM convention. - The element component and any optional modifier component of an ABEM class will NOT be concatenated and instead separated by a space and the modifier classes will be preceded with a single dash
-
character to denote that it is a modifier class. - Each ABEM class should be preceded with a single letter to denote the molecular level of the component followed by a dash
-
character. Examples includea-
for an atomic level component,m-
for a molecular level component,o-
for an organism level component, etc.
If you're not familiar with Brad Frost's atomic design principles I'd recommend his website for further reading.
If we create the same call to action banner component as above but using the ABEM naming methodology we will get something like this:
<div class="m-ctaBanner">
<a class="m-ctaBanner__link -buttonLink -buttonLinkPrimary" href="/buy">Buy now</a>
<a class="m-ctaBanner__link -buttonLink" href="/buy">Book a demo</a>
<a class="m-ctaBanner__link" href="/buy">Learn more</a>
</div>
.m-ctaBanner {
align-items: center;
background-color: #eee;
display: flex;
flex-wrap: wrap;
padding: 40px;
justify-content: center;
}
.m-ctaBanner__link {
color: steelblue;
display: block;
text-decoration: underline;
}
.m-ctaBanner__link.-buttonLink {
border: 2px solid steelblue;
padding: 5px 10px;
text-decoration: none;
}
.m-ctaBanner__link.-buttonLinkPrimary {
background-color: steelblue;
color: black;
}
You'll notice we have less HTML bloat but we've also now got two levels of specificity going on, but is that really a problem? The very nature of a modifier is that it is modifying and overriding something that has come before and therefore it may be more desirable in terms of specificity at the expense of some very minor (as in you'll never notice) performance degradation.
Also notice how much easier it is to distinguish between the block, element, and modifier sections of the class in both the HTML and CSS because the use of camelCase has helped reduced the visual overload of lots and lots of dashes in the overall class name. This becomes more apparent when the block or element or both sections become several words long.
For example which of these looks easier to read:
Regular BEM:curated-grid-media-sbs__grid curated-grid-media-sbs__grid--x-3-col
or ABEM: o-curatedGridMediaSBS__grid -x3Col
We've been using ABEM for several years now but it wasn't until I onboarded one of our new frontend developers that I really started questioning the very first part of the ABEM naming convention - the atomic prefix. When trying to explain why it was there I struggled to actually be able to articulate what purpose it served and the more I thought about it - the more I realised it was often something I found myself battling with. Is this a molecule? Is it an organism? And then it struck me... who cares?! Why does it matter? Why don't we just treat components as components. The atomic design principles are really great but sometimes they can actually get in your way. For example... what is a grid? It's an organism surely? It is an organism that can house molecules and atoms. But in the above example the grid is actually a smaller section of a larger organism which is a side by side presentation of an image or video and then a heading and some copy and a grid of micro messages. So is the grid a molecule inside the organism? Is it an organism inside the organism? All of this is just a distraction from what we're actually trying to do which is make our HTML and CSS simpler. Sometimes one component might include another one and the structure of the parent will change the context in which we view that other component. And so I started thinking there must be a better way. And there is...
Introducing BBEM or Better BEM!
What if we took the efficiencies of ABEM - the atomic block element modifier methodology, and blended them back in to the original CSS BEM - block element modifier methodology and created BBEM or Better BEM?!
At Pivale we are going to be using BBEM as our new standard going forward. So let's rewrite the rules!
- Each component of a BBEM class must use camelCase as per ABEM convention. This means all letters are lowercase apart from the first letter of any subsequent words in a section.
- The block component and element component of a BBEM class will be concatenated with a double underscore
__
as per BEM convention. - The element component and any optional modifier component of a BBEM class will NOT be concatenated and instead separated by a space and the modifier classes will be preceded with a single dash
-
character to denote that it is a modifier class, as per ABEM convention.
This looks like this:
<div class="someAwesomeComponent">
<div class="someAwesomeComponent__nestedElement">
<div class="someAwesomeComponent__nestedElement -withAModifier">
<div class="someAwesomeComponent__nestedElement -withADifferentModifier">
<div class="someAwesomeComponent__differentNestedElement">
</div>
And in CSS we can target as follows:
.someAwesomeComponent {
...
}
.someAwesomeComponent__nestedElement {
...
}
.someAwesomeComponent__nestedElement.-withAModifier {
...
}
.someAwesomeComponent__nestedElement.-withADifferentModifier {
...
}
.someAwesomeComponent__differentNestedElement {
...
}
This is even better when working with SCSS and SCSS nesting:
.someAwesomeComponent {
...
&__nestedElement {
...
&.-withAModifier {
...
}
&.-withADifferentModifier {
...
}
}
&__differentNestedElement {
...
}
}
Can you do this in native CSS nesting? NOPE! And if you want to know more about that you can read my article about native CSS nesting and why we won't be using it all the while we're working with BEM, ABEM or BBEM naming conventions.
BEM CSS vs ABEM CSS vs BBEM CSS conclusion
I love the fact that these sorts of naming conventions and conversations are happening and constantly evolving. Right now BBEM is the big winner for us vs BEM or ABEM. BEM creates HTML bloat and is hard to read. ABEM creates a cognitive block when it comes to making a decision about whether a component is an atom, a molecule, an organism, or a Xenomorph or whatever other names we decide to come up with. BBEM just keeps it simple, less bloated, and more joyous to write HTML and CSS in our projects.
Now I have to seriously weigh up how we can create something like traditional CSS BEM that is easy to work with in native CSS nesting but until I've figured that out, that's all from me! Watch this space!