Uncategorized

Getting the best out of BEM with Sass

Let me be completely honest with you here. When I first read about BEM not so long ago I completely dismissed it and I didn’t like anything about it. Well, the only thing that appealed to me was the fact that people would start to think more holistically and component based. But why use BEM for this? Why do I need to write classes like .header__link--huge? At first, I really didn’t like the idea to name my selectors with bulky class names that consist of prefixes and weird character sequences. But once again my mind got shifted.

Bem with Sass

Before you read any further and if you’re not yet familiar with the basics of BEM I’d like you to first read this great introduction by Mark McDonnell.

Also, if you just want to skip to the Sass part, because you’re bored of all those BEM interpretations, you can just scroll to the bottom of the article.

Because I’ve learned that I should never judge new things before I applied them in practice and because Yandex, BBC and all the others who got down with BEM can’t be all wrong, I forced myself to get my hands dirty and BEM around a bit myself. I started with some simple components and tried to follow all the guidelines and examples that I’ve found on the web.

Quickly I realized that BEM is not about writing class selectors with two underscores and namespace prefixes. BEM is much more than this. It’s a set of very basic rules in a simple but powerful methodology. It’s a way to approach state driven visual design and it’s a powerful communication tool.

BEM stands for “Block”, “Element”, “Modifier”. It is a front-end methodology: a new way of thinking when developing Web interfaces.
Varvara Stepanova

For me BEM’s core strength can be expressed with the following few facts:

  • Great communication tool (designers, developers and even the business have a shared language that is all about blocks, elements and modifiers)
  • Documentation and low complexity (BEM is a great way to structure your CSS so that everyone who knows BEM will understand it immediately)
  • Flat cascade (nesting deeply makes your CSS stinky and unmaintainable)
  • Simple specificity (you just have classes and the order of specification is (almost) your only tool for specificity)
  • Preventing name clashes (with the verbose class naming / prefixing we can easily prevent name clashes and name our elements __title and our modifiers –large safely)

Let’s look at a simple classical BEM example

<section class="login">
  <label class="login__label">
    Username
    <input class="login__input" 
           type="text">
  </label>
  <label class="login__label">
    Password
    <input class="login__input" 
           type="text">
  </label>
  <button class="login__button">Login</button>
</section>
.login {
  margin: 1rem 0;
  padding: 1rem;
  background: lightgray;
}
.login__label {
  display: block;
}
.login__input {
  display: block;
  width: 100%;
  padding: 0.5rem;
  border: none;
  font-size: 1rem;
}
.login__button {
  display: block;
  width: 100%;
  padding: 0.5rem;
  margin-top: 2rem;
  background: gray;
  border: none;
  font-size: 1rem;
}

This might look a little verbose at first but when you start using BEM you quickly realize the benefits of writing CSS like this. One of the key factors for maintainability in software engineering is to keep your dependencies as slim as possible. With a classical BEM approach the only thing that gets in your way are the properties that are inherited automatically though the DOM (like font-size or color). As you don’t nest any selectors you can now easily exchange individual styles, rename selectors and even move elements in the DOM without modifying your style sheet. I think you need to try it yourself in order to feel the difference.

What’s wrong with the classical BEM approach?

After I started to like the basic concept of BEM and I started to feel okay with the verbosity for the sake of maintainability, I had a second round with it and discovered something that made me thinking. I agree that cascading is bad for maintainability and also I agree that it should be kept as flat as possible. But really no cascading at all? Isn’t there anything good about the cascade?

Let’s look at some core principles of BEM:

  • Blocks need to be independent
  • Elements can only exist inside of a block
  • Blocks and elements can be nested in the DOM but the CSS selectors need to be independent
  • Cascading selectors should be avoided

So cascading should be avoided but what about the second statement? If elements can only exist inside of a block doesn’t it make sense to nest element into blocks in CSS too?

If we look at the above simple login block example, let’s assume the following situation. If we fail to sign in, the login block will get a modifier that we call invalid. Based on that we want to change the look of the login block so that the user knows that he needs to resubmit his credentials. Let’s also assume that we need to change the background of the input and the button elements. In order to do so we need to introduce one level of nesting where the element selectors will be nested inside of the parent block modifier selector.

.login--invalid {
  background: salmon;
}
.login--invalid .login__input {
  background: lightsalmon;
}
.login--invalid .login__button {
  background: tomato;
}

Now this puts us into a inconsistent state that I don’t really like. The thing now is that we have two dimensions of specificity for elements. The selector weight as we have two class selectors for the nested element selectors that change their appearance based on the block modifier as well as the order of specification in the CSS. Ideally I would like to have only one dimension for specificity and I prefer the order of specification. If we have multiple block modifiers the order of specification is anyway the only thing we can rely on. So coming back to the statement that elements can’t life without their parent block, would it really harm if we also add the nesting to the base element selectors?

.login {
  margin: 1rem 0;
  padding: 1rem;
  background: lightgray;
}
.login .login__label {
  display: block;
}
.login .login__input {
  display: block;
  width: 100%;
  padding: 0.5rem;
  border: none;
  font-size: 1rem;
}

.login .login__button {
  display: block;
  width: 100%;
  padding: 0.5rem;
  margin-top: 2rem;
  background: gray;
  border: none;
  font-size: 1rem;
}
.login--invalid {
  background: salmon;
}
.login--invalid .login__button {
  background: tomato;
}
.login--invalid .login__input {
  background: lightsalmon;
}

This might look like a detail to you but I believe it’s much cleaner to write BEM like this.

Where is the Sass part your talking about?!?

Okay that’s enough for the BEM basics and my preference of writing BEM. Now I’d like to talk about tooling for BEM. Writing BEM, even if the code is much more efficient and maintainable, can be painful. Specially when you have a lot of modifiers in your project. Also pseudo classes and media queries can be considered modifiers and need to have their place in a proper workflow. Doing all this in pure CSS can be an overkill. For this reason I have created a simple BEM Sass library that we currently use in a project for a client. It’s really simple to use and brings very clean and consistent code.

You can get the Sass code that is used in the following examples from codepen: http://codepen.io/gionkunz/pen/rkswl?editors=010

Using this Sass library you could express the above example with the following SCSS code:

%login-input-base {
  display: block;
  width: 100%;
  padding: 0.5rem;
  border: none;
  font-size: 1rem;
}

@include bem-block(login) {
  margin: 1rem 0;
  padding: 1rem;
  background: lightgray;
  
  @include bem-element(label) {
    display: block;
  }
  
  @include bem-element(input) {
    @extend %login-input-base;
  }
  
  @include bem-element(button) {
    @extend %login-input-base;
    margin-top: 2rem;
    background: gray;
  }
}

@include bem-block(login, $modifier: invalid) {
  background: salmon;
  
  @include bem-element(input) {
    background: lightsalmon;
  }
  
  @include bem-element(button) {
    background: tomato;
  }
}

Isn’t that already much cleaner and more readable? You can add as many block modifiers as you like by just adding an other bem-block include and include any elements you like to style there. This gets even more obvious when you also include element modifiers, pseudo classes on blocks that should influence child element and media queries.

%login-input-base {
  display: block;
  width: 100%;
  padding: 0.5rem;
  border: none;
  font-size: 1rem;
}

@include bem-block(login) {
  margin: 1rem 0;
  padding: 1rem;
  background: lightgray;
  
  @include bem-element(label) {
    display: block;
  }
  
  @include bem-element(input) {
    @extend %login-input-base;
  }
  
  @include bem-element(input, $modifier: invalid) {
    border: 1px solid red;
  }
  
  @include bem-element(button) {
    @extend %login-input-base;
    margin-top: 2rem;
    background: gray;
  }
}

@include bem-block(login, $modifier: invalid) {
  background: salmon;
  
  @include bem-element(input) {
    background: lightsalmon;
  }
  
  @include bem-element(button) {
    background: tomato;
  }
}

@include bem-block(login, $pseudo-class: hover) {
  box-shadow: 0 0 10px gray;
  
  @include bem-element(input) {
    box-shadow: 0 0 5px gray;
  }
  
  @include bem-element(button) {
    box-shadow: 0 0 5px gray;
  }
}

@include bem-block(login, $modifier: invalid, $pseudo-class: hover) {
  box-shadow: 0 0 10px red;
  
  @include bem-element(input) {
    box-shadow: 0 0 5px red;
  }
  
  @include bem-element(button) {
    box-shadow: 0 0 5px red;
  }
}

@include bem-block(login, $media: "screen and (min-width: 1024px)") {
  @include bem-element(label) {
    font-size: 1.5rem;
  }
  
  @include bem-element(input) {
    font-size: 1.5rem;
  }
  
  @include bem-element(button) {
    display: inline-block;
    width: auto;
    padding: 1rem;
    font-size: 1.5rem;
  }
}

This code generates the following CSS code:

.login .login__input, .login .login__button {
  display: block;
  width: 100%;
  padding: 0.5rem;
  border: none;
  font-size: 1rem;
}

.login {
  margin: 1rem 0;
  padding: 1rem;
  background: lightgray;
}
.login .login__label {
  display: block;
}
.login .login__input--invalid {
  border: 1px solid red;
}
.login .login__button {
  margin-top: 2rem;
  background: gray;
}

.login--invalid {
  background: salmon;
}
.login--invalid .login__input {
  background: lightsalmon;
}
.login--invalid .login__button {
  background: tomato;
}

.login:hover {
  box-shadow: 0 0 10px gray;
}
.login:hover .login__input {
  box-shadow: 0 0 5px gray;
}
.login:hover .login__button {
  box-shadow: 0 0 5px gray;
}

.login--invalid:hover {
  box-shadow: 0 0 10px red;
}
.login--invalid:hover .login__input {
  box-shadow: 0 0 5px red;
}
.login--invalid:hover .login__button {
  box-shadow: 0 0 5px red;
}

@media screen and (min-width: 1024px) {
  .login .login__label {
    font-size: 1.5rem;
  }
  .login .login__input {
    font-size: 1.5rem;
  }
  .login .login__button {
    display: inline-block;
    width: auto;
    padding: 1rem;
    font-size: 1.5rem;
  }
}

You can also checkout the above example on jsbin: http://jsbin.com/fucom/5/edit

Final notes

After a strong aversion against BEM I really started to love it. Even though it solves a lot of technical issues and establishes clean and maintainable code I believe that the biggest benefit of using BEM is for the sake of communication. Communicate about the same things is so important and in large teams, with designers, developers and clients it’s a real efficiency boost when you have a unified language like this.

I think BEM is solving a lot of problems and it allows room for interpretation and variation which I believe is very important for adoption.

Standard

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s