BEM (Block-Element-Modifier) in CSS is an industry standard in web development; I know a lot of people who use it. I learned it, mastered it and started using it in my own projects.
However, like all systems and tools I've used, I started to question it. Was BEM really the prized gem that I've always thought it was?
In today's blog post, I would like to talk about BEM, why I used it, and why I don't use it anymore.
What I like about BEM
BEM was magic when I first discovered it. It solved a few issues:
- Scoping: This is the number one problem it solves โ scope leaks. Scope leaks can cause your CSS to break in unpredictable ways.
- Name clashes: With BEM, you can rest assured that no two elements would have the same name on the same page.
- Readable: BEM names were easy to understand and to trace down in HTML.
- Less cascade conflicts: Because every element had a different name, a lot of conflicts disappeared.
- Modularity: Adding styles does not break existing styles.
What I hate about BEM
With all the advantages listed above, I thought BEM was the perfect solution. Until I used it. Then I used it again. And again. And again.
Then I noticed a few shortcomings:
- Long names: BEM names were just too long. And they only got longer and longer the more elements you add to your page. (Kinda like O(n) in algorithmic complexity lingo.)
- Code is not DRY: Don't Repeat Yourself is important to all developers when it can be achieved. This should be no exception.
- Ugly code: Not only was the code long and repetitive, it was also unpleasing to look at.
There was no cure for these issues; they are all inherent in BEM.
So you could imagine how elated (and surprised) I was to find a solution.
Introducing GPS
As I was looking for a solution to the shortcomings of BEM, I stumbled upon an article titled BEM is terrible.
In that article, the author introduced the concept of GPS: Global, Page (or View), Section. I have never heard of it, but I was intrigued to find out that it's a new system that could be better than BEM.
However, after reading the whole article, I discovered that the method is not new at all. In fact, it sounds more like how these technologies were supposed to be used.
In summary, the foundation of what he is saying is this:
- Use IDs in CSS when you need to reference HTML elements that are unique (similar to every student in school having an ID).
- Use class names in CSS when you need to reference HTML elements that will be repeated in the HTML document, or have a repeated style (similar to every student in school being grouped by classes).
If you follow these principles, everything falls in line. I used to think that IDs were evil because of specificity, but this could not be further from the truth. CSS becomes more powerful when you learn to embrace its strengths.
Usage examples of GPS
My blog
With BEM, the code for my blog home page was looking like this
<div class="feed">
<article class="feed__item">...</article>
<article class="feed__item">...</article>
<article class="feed__item">...</article>
<article class="feed__item">...</article>
</div>
With GPS, you'd have something simpler:
<div id="feed">
<article>...</article>
<article>...</article>
<article>...</article>
<article>...</article>
</div>
You see? Shorter names. Less bytes. Less cognitive load. More communication. No clutter. It's genius.
Now in CSS, instead of this with BEM:
.feed {
/* CSS styles here */
}
.feed__item {
/* CSS styles here */
}
I do this with GPS:
#feed {
/* CSS styles here */
}
#feed > article {
/* CSS styles here */
}
A client's website
Below is a simplified version of code I wrote for a client of mine using BEM:
<div class="appointment-summary">
<h2>Appointment summary</h2>
<div class="appointment-summary__cart">
<div class="appointment-summary__cart__summary">
<p>2 services</p>
<div class="appointment-summary__cart__summary__details">
<div class="container">
<span>$175.00</span>
<svg class="separator" viewBox="0 0 24 24">...</svg>
<div class="appointment-summary__cart__summary__details__duration">
<svg class="icon icon--time" viewBox="0 0 24 24">...</svg>
<span>2 hr</span>
</div>
</div>
<div class="appointment-summary__cart__summary__details__buttons">
<button class="button--arrow-toggle"><svg class="icon icon--chevron-up">...</svg></button>
<a id="book-appointment-mobile-button" href="booking-complete.html" class="button button--primary button--next button--book-appointment">Book Appointment</a>
</div>
</div>
</div>
<div class="appointment-summary__cart__services">
<div class="appointment-summary__cart__services__service">
<p>Wash and Blowdry</p>
<span>$55.00</span>
<svg class="icon icon--trash">...</svg>
</div>
<div class="appointment-summary__cart__services__service">
<p>Jumbo Box Braids</p>
<span>$120.00</span>
<svg class="icon icon--trash">...</svg>
</div>
</div>
</div>
<a id="book-appointment-button" href="booking-complete.html" class="button button--primary button--next button--book-appointment">Book Appointment</a>
</div>
Using GPS, a lot of complexity goes away:
<div id="appointment-summary">
<h2>Appointment summary</h2>
<div id="cart">
<div id="summary">
<p>2 services</p>
<div id="summary-details">
<div class="row">
<span>$175.00</span>
<svg class="separator">...</svg>
<div id="summary-duration">
<svg class="icon time">...</svg>
<span>2 hr</span>
</div>
</div>
<div id="summary-buttons">
<button class="arrow-toggle"><svg class="icon chevron-up">...</svg></button>
<a id="book-appointment-mobile-button"
href="booking-complete.html"
class="button primary next book-appointment">
Book Appointment</a>
</div>
</div>
</div>
<div id="cart-services">
<div class="service">
<p>Wash and Blowdry</p>
<span>$55.00</span>
<svg class="icon trash">...</svg>
</div>
<div class="service">
<p>Jumbo Box Braids</p>
<span>$120.00</span>
<svg class="icon trash">...</svg>
</div>
</div>
</div>
<a id="book-appointment-button"
href="booking-complete.html"
class="button primary next book-appointment">
Book Appointment</a>
</div>
The CSS with BEM:
.appointment-summary {
/* CSS styles here */
}
.appointment-summary h2 {
/* CSS styles here */
}
.appointment-summary__cart {
/* CSS styles here */
}
.appointment-summary__cart__summary {
/* CSS styles here */
}
.appointment-summary__cart__summary p {
/* CSS styles here */
}
.appointment-summary__cart__summary__details {
/* CSS styles here */
}
.appointment-summary__cart__summary__details .container {
/* CSS styles here */
}
.appointment-summary__cart__summary__details span {
/* CSS styles here */
}
.icon {
/* CSS styles here */
}
.icon--chevron-up {
/* CSS styles here */
}
.appointment-summary__cart__summary__details__duration {
/* CSS styles here */
}
.appointment-summary__cart__summary__details__duration span {
/* CSS styles here */
}
.appointment-summary__cart__summary__details__buttons {
/* CSS styles here */
}
.appointment-summary__cart__services {
/* CSS styles here */
}
.appointment-summary__cart__services__service {
/* CSS styles here */
}
.appointment-summary__cart__services__service p {
/* CSS styles here */
}
.appointment-summary__cart__services__service span {
/* CSS styles here */
}
With GPS:
#appointment-summary {
/* CSS styles here */
}
#appointment-summary h2 {
/* CSS styles here */
}
#appointment-summary #cart {
/* CSS styles here */
}
#appointment-summary #summary {
/* CSS styles here */
}
#appointment-summary #summary p {
/* CSS styles here */
}
#appointment-summary #summary-details {
/* CSS styles here */
}
#appointment-summary #summary-details > .row {
/* CSS styles here */
}
#appointment-summary #summary-details > span {
/* CSS styles here */
}
#appointment-summary .icon {
/* CSS styles here */
}
#appointment-summary .chevron-up {
/* CSS styles here */
}
#appointment-summary #summary-duration {
/* CSS styles here */
}
#appointment-summary #summary-duration > span {
/* CSS styles here */
}
#appointment-summary #summary-buttons {
/* CSS styles here */
}
#appointment-summary #services {
/* CSS styles here */
}
#appointment-summary #services > .service {
/* CSS styles here */
}
#appointment-summary #services > .service p {
/* CSS styles here */
}
#appointment-summary #services > .service span {
/* CSS styles here */
}
There you have it. Shorter. Simpler. Clearer. ๐
Few criticisms on GPS
One thing I dislike about GPS is the recommendation of prefixes like g-
for global styles and p-
for page styles. I believe that prefixes are unnecessary, superfluous and make my CSS a little less readable.
I think the best thing is to allow these styles to be inferred by their context.
For instance, an About page may be structured like this:
<html>
<body id="about">
<div id="intro">
<img src="images/our-team.webp">
<p class="g-full-width">Meet our team</p>
</div>
</body>
</html>
The g-
in g-full-width
indicates that the values are read from the global styles.
However, I would argue that there is no difference between defining the selector as full-width
(as opposed to g-full-width
). I should not really care about where the value comes from. If I wanted to overwrite the value for some reason, I could use IDs to do so:
/* In global CSS */
.full-width {
/* Full width in global context */
}
/* In page CSS */
#about .full-width {
/* Full width in about page context */
}
I've never tried this before, but I plan to give it a shot and see if I run into any roadblocks. I believe this is a much simpler approach and could make my CSS code even more readable.
The bottom line
It's sad how I overlooked the basic principles of the web and dabbled into something that made using the web harder. ๐คฆ
Oh well, I'm happy to have found a way that works best for me. ๐