Home » NewsBlog » ABEM. A more useful adaptation of BEM.

ABEM. A more useful adaptation of BEM.

BEM (Block Element Modifier) is a popular CSS class naming convention that makes CSS easier to maintain. This article assumes that you are already familiar with the naming convention. If not you can learn more about it at getbem.com to catch up on the basics.

The standard syntax for BEM is:

block-name__element-name--modifier-name

I’m personally a massive fan of the methodology behind the naming convention. Separating your styles into small components is far easier to maintain than having a sea of high specificity spread all throughout your stylesheet. However, there are a few problems I have with the syntax that can cause issues in production as well as cause confusion for developers. I prefer to use a slightly tweaked version of the syntax instead. I call it ABEM (Atomic Block Element Modifier):

[a/m/o]-blockName__elementName -modifierName

An Atomic Design Prefix

The a/m/o is an Atomic Design prefix. Not to be confused with Atomic CSS which is a completely different thing. Atomic design is a methodology for organizing your components that maximizes the ability to reuse code. It splits your components into three folders: atoms, molecules, and organisms. Atoms are super simple components that generally consist of just a single element (e.g. a button component). Molecules are small groups of elements and/or components (e.g. a single form field showing a label and an input field). Organisms are large complex components made up of many molecule and atom components (e.g. a full registration form).

An Atomic Design Diagram showing atoms inside molecules inside an organism

The difficulty of using atomic design with classic BEM is that there is no indicator saying what type of component a block is. This can make it difficult to know where the code for that component is since you may have to search in 3 separate folders in order to find it. Adding the atomic prefix to the start makes it immediately obvious what folder the component is stored in.

camelCase

It allows for custom grouping

Classic BEM separates each individual word within a section with a single dash. Notice that the atomic prefix in the example above is also separated from the rest of the class name by a dash. Take a look at what happens now when you add an atomic prefix to BEM classic vs camelCase:

/* classic + atomic prefix */
.o-subscribe-form__field-item {}

/* camelCase + atomic prefix */
.o-subscribeForm__fieldItem {}

At a glance, the component name when reading the classic method looks like it’s called “o subscribe form”. The significance of the “o” is completely lost. When you apply the “o-” to the camelCase version though, it is clear that it was intentionally written to be a separate piece of information to the component name.

Now you could apply the atomic prefix to classic BEM by capitalizing the “o” like this:

/* classic + capitalized atomic prefix */
.O-subscribe-form__field-item {}

That would solve the issue of the “o” getting lost amongst the rest of the class name however it doesn’t solve the core underlying issue in the classic BEM syntax. By separating the words with dashes, the dash character is no longer available for you to use as a grouping mechanism. By using camelCase, it frees you up to use the dash character for additional grouping, even if that grouping is just adding a number to the end of a class name.

Your mind will process the groupings faster

camelCase also has the added benefit of making the grouping of the class names easier to mentally process. With camelCase, every gap you see in the class name represents a grouping of some sort. In classic BEM, every gap could be either a grouping or a space between two words in the same group.

Take a look at this silhouette of a classic BEM class (plus atomic prefix) and try to figure out where the prefix, block, element and modifier sections start and end:

Ok, now try this one. It is the exact same class as the one above except this time it is using camelCase to separate each word instead of dashes:

That was much easier wasn’t it? Those silhouettes are essentially what your mind sees when it is scanning through your code. Having all those extra dashes in the class name make the groupings far less clear. As you read through your code, your brain tries to process weather the gaps it encounters are new groupings or just new words. This lack of clarity causes cognitive load to weigh on your mind as you work.

classic BEM + atomic prefix
camelCase BEM + atomic prefix

Use multi class selectors (responsibly)

One of the golden rules in BEM is that every selector is only supposed to contain a single class. The idea is that it keeps CSS maintainable by keeping the specificity of selectors low and manageable. On the one hand, I agree that low specificity is preferable over having specificity run rampant. On the other, I strongly disagree that a strict one class per selector rule is the best thing for projects. Using some multi-class selectors in your styles can actually improve maintainability rather than diminish it.

“But it leads to higher specificity! Don’t you know that specificity is inherently evil?!?”

Specificity != bad.

Uncontrolled specificity that has run wild = bad.

Having some higher specificity declarations doesn’t instantly mean that your CSS is more difficult to maintain. If used in the right way, giving certain rules higher specificity can actually make CSS easier to maintain. The key to writing maintainable CSS with uneven specificity is to add specificity purposefully and not just because a list item happens to be inside a list element.

Besides, don’t we actually want our modifier styles to have greater power over elements than default styles? Bending over backwards to keep modifier styles at the same specificity level as normal styles seems silly to me. When do you actually want your regular default styles to override your specifically designated modifier styles?

Separating the modifier leads to cleaner HTML

This is the biggest change to the syntax that ABEM introduces. Instead of connecting the modifier to the element class, you apply it as a separate class.

One of the things that practically everyone complains about when they first start learning BEM is how ugly it is. It is especially bad when it comes to modifiers. Take a look at this atrocity. It only has three modifiers applied to it and yet it looks like a train wreck:

B__E–M:

Look at all that repetition! That repetition makes it pretty difficult to read what it’s actually trying to do. Now take a look at this ABEM example that has all the same modifiers as the previous example:

A-B__E -M:

Much cleaner isn’t it? It is far easier to see what those modifier classes are trying to say without all that repetitive gunk getting in the way.

When inspecting an element with browser DevTools, you still see the full rule in the styling panel so it retains the connection to the original component in that way:

.a-blockName__elementName.-green {
  background: green;
  color: white;
}

It’s not much different to the BEM equivalent

.block-name__element-name--green {
  background: green;
  color: white;
}

Managing state becomes easy

One large advantage that ABEM has over classic BEM is that it becomes immensely easier to manage the state of a component. Let’s use a basic accordion as an example. When a section of this accordion is open, let’s say that we want to apply these changes to the styling:

  • Change the background colour of the section heading
  • Display the content area
  • Make a down arrow point up

We are going to stick to the classic B__E–M syntax for this example and strictly adhere to the one class per css selector rule. This is what we end up with (note, that for the sake of brevity, this accordion is not accessible):

See the Pen Accordion 1 – Pure BEM by Daniel Tonon (@daniel-tonon) on CodePen.

The SCSS looks pretty clean but take a look at all the extra classes that we have to add to the HTML for just a single change in state!

HTML while a segment is closed using BEM:

Three

Lorem ipsum dolor sit amet...

HTML while a segment is open using BEM:

One

Lorem ipsum dolor sit amet...

Now let’s take a look at what happens when we switch over to using this fancy new A-B__E -M method:

See the Pen Accordion 2 – ABEM alternative by Daniel Tonon (@daniel-tonon) on CodePen.

A single class now controls the state-specific styling for the entire component now instead of having to apply a separate class to each element individually.

HTML while a segment is open using ABEM:

One

Lorem ipsum dolor sit amet...

Also, take a look at how much simpler the javascript has become. I wrote the JavaScript as cleanly as I could and this was the result:

JavaScript when using pure BEM:

class revealer {
  constructor(el){
    Object.assign(this, {
      $wrapper: el,
      targets: ['trigger', 'icon', 'content'],
      isOpen: false,
    });
    this.gather_elements();
    this.$trigger.onclick = ()=> this.toggle();
  }

  gather_elements(){
    const keys = this.targets.map(selector => `$${selector}`);
    const elements = this.targets.map(selector => {
      return this.$wrapper.querySelector(`.revealer__${selector}`);
    });
    let elObject = {};
    keys.forEach((key, i) => {
      elObject[key] = elements[i];
    });
    Object.assign(this,	elObject);
  }
  
  toggle(){
    if (this.isOpen) {
      this.close();
    } else {
      this.open();
    }
  }

  open(){
    this.targets.forEach(target => {
      this[`$${target}`].classList.add(`revealer__${target}--open`);
    })
    this.isOpen = true;
  }

  close(){
    this.targets.forEach(target => {
      this[`$${target}`].classList.remove(`revealer__${target}--open`);
    })
    this.isOpen = false;
  }
}

document.querySelectorAll('.revealer').forEach(el => {
  new revealer(el);
})

JavaScript when using ABEM:

class revealer {
  constructor(el){
    Object.assign(this, {
      $wrapper: el,
      isOpen: false,
    });
    this.$trigger = this.$wrapper.querySelector('.m-revealer__trigger');
    this.$trigger.onclick = ()=> this.toggle();
  }
  
  toggle(){
    if (this.isOpen) {
      this.close();
    } else {
      this.open();
    }
  }

  open(){
    this.$wrapper.classList.add(`-open`);
    this.isOpen = true;
  }

  close(){
    this.$wrapper.classList.remove(`-open`);
    this.isOpen = false;
  }
}

document.querySelectorAll('.m-revealer').forEach(el => {
  new revealer(el);
})

This was just a very simple accordion example. Think about what happens when you extrapolate this out to something like a sticky header that changes when sticky. A sticky header might need to tell 5 different components when the header is sticky. Then in each of those 5 components, 5 elements might need to react to that header being sticky. That’s 25 element.classList.add("[componentName]__[elementName]--sticky") rules we would need to write in our js to strictly adhere to the BEM naming convention. What makes more sense? 25 unique classes that are added to every element that is affected, or just one -sticky class added to the header that all 5 elements in all 5 components are able to access and read easily?

The BEM “solution” is completely impractical. Applying modifier styling to large complex components ends up turning into a bit of a grey area. A grey area that causes confusion for any developers trying to strictly adhere to the BEM naming convention as closely as possible.

ABEM modifier issues

Separating the modifier isn’t without its flaws. However, there are some simple ways to work around those flaws.

Issue 1: Nesting

So we have our accordion and it’s all working perfectly. Later down the line, the client wants to nest a second accordion inside the first one. So you go ahead and do that… this happens:

See the Pen Accordion 3 – ABEM nesting bug by Daniel Tonon (@daniel-tonon) on CodePen.

Nesting a second accordion inside the first one causes a rather problematic bug. Opening the parent accordion also applies the open state styling to all of the child accordions in that segment.

This is something that you obviously don’t want to happen. There is a good way to avoid this though.

To explain it, let’s play a little game. Assuming that both of these CSS rules are active on the same element, what color do you think that element’s background would be?

.-green > * > * > * > * > * > .element {
  background: green;
}

.element.-blue {
  background: blue;
}

If you said green due to the first rule having a higher specificity than the second rule, you would actually be wrong. Its background would be blue.

Fun fact: * is the lowest specificity selector in CSS. It basically means “anything” in CSS. It actually has no specificy, meaning it doesn’t add any specificity to a selector you add it to. That means that even if you used a rule that consisted of a single class and 5 stars (.element > * > * > * > * > *) it could still be easily overwritten by just a single class on the next line of CSS!

We can take advantage of this little CSS quirk to create a more targeted approach to the accordion SCSS code. This will allow us to safely nest our accordions.

See the Pen Accordion 4 – ABEM nesting bug fix by Daniel Tonon (@daniel-tonon) on CodePen.

By using the .-modifierName > * > & pattern, you can target direct descendants that are multiple levels deep without causing your specificity to get out of control.

I only use this direct targeting technique as it becomes necessary though. By default, when I’m writing ABEM, I’ll write it how I did in that original ABEM accordion example. The non-targeted method is generally all that is needed in most cases. The problem with the targeted approach is that adding a single wrapper around something can potentially break the whole system. The non-targeted approach doesn’t suffer from this problem. It is much more lenient and prevents the styles from breaking if you ever need to alter the HTML later down the line.

Issue 2: Naming collisions

An issue that you can run into using the non-targeted modifier technique is naming collisions. Let’s say that you need to create a set of tabs and each tab has an accordion in it. While writing this code, you have made both the accordion and the tabs respond to the -active class. This leads to a name collision. All accordions in the active tab will have their active styles applied. This is because all of the accordions are children of the tab container elements. It is the tab container elements that have the actual -active class applied to them. (Neither the tabs nor the accordion in the following example are accessible for the sake of brevity.)

See the Pen Accordion in tabs 1 – broken by Daniel Tonon (@daniel-tonon) on CodePen.

Now one way to resolve this conflict would be to simply change the accordion to respond to an -open class instead of an -active class. I would actually recommend that approach. For the sake of an example though, let’s say that isn’t an option. You could use the direct targeting technique mentioned above, but that makes your styles very brittle. Instead what you can do is add the component name to the front of the modifier like this:

.o-componentName {
  &__elementName {
    .-componentName--modifierName & {
      /* modifier styles go here */
    }
  }
}

The dash at the front of the name still signifies that it is a modifier class. The component name prevents namespace collisions with other components that should not be getting affected. The double dash is mainly just a nod to the classic BEM modifier syntax to double reinforce that it is a modifier class.

Here is the accordion and tabs example again but this time with the namespace fix applied:

See the Pen Accordion in tabs 2 – fixed by Daniel Tonon (@daniel-tonon) on CodePen.

I recommend not using this technique by default though mainly for the sake of keeping the HTML clean and also to prevent confusion when multiple components need to share the same modifier.

The majority of the time, a modifier class is being used to signify a change in state like in the accordion example above. When an element changes state, all child elements, no matter what component they belong to, should be able to read that state change and respond to it easily. When a modifier class is intended to affect multiple components at once, confusion can arise around what component that modifier specifically belongs to. In those cases, name-spacing the modifier does more harm than good.

ABEM modifier technique summary

So to make the best use of the ABEM modifier, use .-modifierName & or &.-modifierName syntax by default (depends on what element has the class on it)

.o-componentName {
  &.-modifierName {
    /* componentName modifier styles go here */
  }

&__elementName {
    .-modifierName & {
      /* elementName modifier styles go here */
    }
  }
}

Use direct targeting if nesting a component inside itself is causing an issue.

.o-componentName {
  &__elementName {
    .-nestedModifierName > * > & {
      /* modifier styles go here */
    }
  }
}

Use the component name in the modifier if you run into shared modifier name collisions. Only do this if you can’t think of a different modifier name that still makes sense.

.o-componentName {
  &__elementName {
    .-componentName--sharedModifierName & {
      /* modifier styles go here */
    }
  }
}

Context sensitive styles

Another issue with strictly adhering to the BEM one class per selector methodology is that it doesn’t allow you to write context sensitive styles.

Context sensitive styles are basically “if this element is inside this parent, apply these styles to it”.

With context sensitive styles, there is a parent component and a child component. The parent component should be the one that applies layout related styles such as margin and position to the child component (.parent .child { margin: 20px }). The child component should always by default not have any margin around the outside of the component. This allows the child components to be used in more contexts since it is the parent in charge of it’s own layout rather than its children.

Just like with real parenting, the parents are the ones who should be in charge. You shouldn’t let their naughty clueless children call the shots when it comes to the parents layout.

To dig further into this concept, let’s pretend that we are building a fresh new website and right now we are building the subscribe form component for the site.

See the Pen Context sensitive 1 – IE unfriendly by Daniel Tonon (@daniel-tonon) on CodePen.

This is the first time we have had to put a form on this awesome new site that we are building. We want to be like all the cool kids so we used CSS grid to do the layout. We’re smart though. We know that the button styling is going to be used in a lot more places throughout the site. To prepare for this, we separate the subscribe button styles into its own separate component like good little developers.

A while later we start cross-browser testing. We open up IE11 only to see this ugly thing staring us in the face:

IE11 does kind of support CSS grid but it doesn’t support grid-gap or auto placement. After some cathartic swearing and wishing people would update their browsers, you adjust the styles to look more like this:

See the Pen Context sensitive 2 – what not to do by Daniel Tonon (@daniel-tonon) on CodePen.

Now it looks perfect in IE. All is right with the world. What could possibly go wrong?

A couple of hours later you are putting this button component into a different component on the site. This other component also uses css-grid to layout its children.

You write the following code:

See the Pen Context sensitive 3 – the other component by Daniel Tonon (@daniel-tonon) on CodePen.

You expect to see a layout that looks like this even in IE11:

But instead, because of the grid-column: 3; code you wrote earlier, it ends up looking like this:

Yikes! So what do we do about this grid-column: 3; CSS we wrote earlier? We need to restrict it to the parent component but how should we go about doing that?

Well the classic BEM method of dealing with this is to add a new parent component element class to the button like this:

See the Pen Context sensitive 4 – classic BEM solution by Daniel Tonon (@daniel-tonon) on CodePen.

On the surface this solution looks pretty good:

  • It keeps specificity low
  • The parent component is controlling its own layout
  • The styling isn’t likely to bleed into other components we don’t want it to bleed into

Everything is awesome and all is right with the world… right?

The downside of this approach is mainly due to the fact that we had to add an extra class to the button component. Since the subscribe-form__submit class doesn’t exist in the base button component, it means that we need to add extra logic to whatever we are using as our templating engine for it to receive the correct styles.

I love using Pug to generate my page templates. I’ll show you what I mean using Pug mixins as an example.

First, here is the original IE unfriendly code re-written in mixin format:

See the Pen Context sensitive 5 – IE unfriendly with mixins by Daniel Tonon (@daniel-tonon) on CodePen.

Now lets add that IE 11 subscribe-form__submit class to it:

See the Pen Context sensitive 6 – IE safe BEM solution with mixins by Daniel Tonon (@daniel-tonon) on CodePen.

That wasn’t so hard, so what am I complaining about? Well now let’s say that we sometimes want this module to be placed inside a sidebar. When it is, we want the email input and the button to be stacked on top of one another. Remember that in order to strictly adhere to BEM, we are not allowed to use anything higher in specificity than a single class in our styles.

See the Pen Context sensitive 7 – IE safe BEM with mixins in sidebar by Daniel Tonon (@daniel-tonon) on CodePen.

That Pug code isn’t looking so easy now is it? There are a few things contributing to this mess.

  1. Container queries would make this far less of a problem but they don’t exist yet natively in any browser
  2. The problems around the BEM modifier syntax are rearing their ugly heads.

Now lets try doing it again but this time using context sensitive styles:

See the Pen Context sensitive 8 – IE safe Context Sensitive with mixins in sidebar by Daniel Tonon (@daniel-tonon) on CodePen.

Look at how much simpler the Pug markup has become. There is no “if this then that” logic to worry about in the pug markup. All of that parental logic is passed off to the css which is much better at understanding what elements are parents of other elements anyway.

You may have noticed that I used a selector that was three classes deep in that last example. It was used to apply 100% width to the button. Yes a three class selector is ok if you can justify it.

I didn’t want 100% width to be applied to the button every time it was:

  • used at all anywhere
  • placed inside the subscribe form
  • placed inside the side-bar

I only wanted 100% width to be applied when it was both inside the subscribe form and inside the sidebar. The best way to handle that was with a three class selector.

Ok, in reality, I would more likely use an ABEM style -verticalStack modifier class on the subscribe-form element to apply the vertical stack styles or maybe even do it through element queries using EQCSS. This would mean that I could apply the vertical stack styles in more situations than just when it’s in the sidebar. For the sake of an example though, I’ve done it as context sensitive styles.

Now that we understand context sensitive styles, let’s go back to that original example I had and use some context sensitive styles to apply that troublesome grid-column: 3 rule:

See the Pen Context sensitive 9 – context sensitive method with mixins by Daniel Tonon (@daniel-tonon) on CodePen.

Context sensitive styles lead to simpler HTML and templating logic whilst still retaining the reusability of child components. BEM’s one class per selector philosophy doesn’t allow for this to happen though.

Since context sensitive styles are primarily concerned with layout, depending on circumstances, you should generally use them whenever you are dealing with these CSS properties:

  • Anything CSS grid related that is applied to the child element (grid-column, grid-row etc.)
  • Anything flexbox related that is applied to the child element (flex-grow, flex-shrink, align-self etc.)
  • margin values greater than 0
  • position values other than relative (along with the top, left, bottom, and right properties)
  • transform if it is used for positioning like translateY

You may also want to place these properties into context-sensitive styles but they aren’t as often needed in a context sensitive way.

  • width
  • height
  • padding
  • border

To be absolutely clear though, context sensitive styles are not nesting for the sake of nesting. You need to think of them as if you were writing an if statement in JavaScript.

So for a CSS rule like this:

.parent .element {
  /* context sensitive styles */
}

You should think of it like you are writing this sort of logic:

if (.element in .parent) {
  .element { /* context sensitive styles */ }
}

Also understand that writing a rule that is three levels deep like this:

.grandparent .parent .element {
  /* context sensitive styles */
}

Should be thought of like you are writing logic like this:

if (
    (.element in .parent) &&
    (.element in .grandparent) &&
    (.parent in .grandparent)
  ) {
  .element { /* context sensitive styles */ }
}

So by all means, write a css selector that is three levels deep if you really think you need that level of specificity. Please understand the underlying logic of the css that you are writing though. Only use a level of specificity that makes sense for the particular styling that you are trying to achieve.

And again, one more time, just to be super clear, do not nest for the sake of nesting!

Summing Up

The methodology behind the BEM naming convention is something that I wholeheartedly endorse. It allows css to be broken down into small easily manageable components rather than leaving css in an unwieldy mess of high specificity that is difficult to maintain. The official syntax for BEM has a lot to be desired though.

The official BEM syntax:

  • Doesn’t support Atomic Design
  • Is unable to be extended easily
  • Takes longer for your mind to process the grouping of the class names
  • Is horribly incompetent when it comes to managing state on large components
  • Tries to encourage you to use single class selectors when double class selectors lead to easier maintainability
  • Tries to name-space everything even when namespacing causes more problems than it solves.
  • Makes HTML extremly bloated when done properly

My unofficial ABEM approach:

  • Makes working with Atomic Design easier
  • Frees up the dash character as an extra method that can be used for grouping
  • Allows your mind to process the grouping of the class names faster
  • Is excellent at handling state on any sized component no matter how many sub components it has
  • Encourages controlled specificity rather than just outright low specificity to mitigate team confusion and improve site maintainability
  • Avoids namespacing when it isn’t needed
  • Keeps HTML quite clean with minimal extra classes applied to modules while still retaining all of BEM’s advantages

Disclaimer

I didn’t invent the -modifier (single dash before the modifier name) idea. I discovered it in 2016 from reading an article. I can’t remember who originally conceptualized the idea. I’m happy to credit them if anyone knows the article.


ABEM. A more useful adaptation of BEM. is a post from CSS-Tricks

CSS Tricks Go to Source
Author: Daniel Tonon

Powered by WPeMatico

2017-12-14T03:01:50+00:00December 14th, 2017|Categories: News, Tutorials|Tags: |