BEM modifiers: multiple classes vs @extend
TL;DR multiple classes is ok, but restrict yourself to a single modifier class and a single state class or state data attribute per element:
<div class="comment comment--admin is-disabled">or<div class="comment comment--admin" data-state="disabled">
For a while I've advocated making modifier classes standalone, like so:
.commentcolor: $black.comment--admin@extend .commentbackground-color: $green
<div class="comment">...</div><div class="comment--admin">...</div>
I think I was wrong. Here's why I now prefer using multiple classes on an element.
Modified modules aren't the norm
comment--admin class up there is actually a bit of a contrived example. Most modules don't need a "same but slightly different" version. Instead it's usually just...
// the base module.commentcolor: $black// a few scoped state classes & pseudoclasses&:hover,&:focuscolor: $blue&.is-disabledcolor: $grey// & maybe some modifications based on// Modernizr or Metaquery classes on <html>.no-svg &background-image: url('lame.png').breakpoint-desktop &font-size: 20px
When you do need a slightly modified version of your module, there's not really much point @extend-ing all of those selectors just to avoid an extra class in your HTML. A modifier class that only updates the properties you need to update is fine:
.commentbackground-color: $whitecolor: $black.comment--adminbackground-color: $green
<div class="comment comment--admin">
The extra class only modifies the properties you need to modify, but otherwise you keep inheriting all the defaults from the default version.
Multiple classes means source order matters
This is probably the main reason I've avoided chaining multiple classes together until now.
.commentcolor: $black.comment--admincolor: $green.comment--importantcolor: $yellowfont-weight: bold
<div class="comment comment--admin comment--important">
If your modifier classes modify the same property, the last one you define wins. It's really easy to clobber another modifier's styles or end up with a brittle module that you can't refactor without eyeballing all the places you use it.
Luckily that's an easy problem to solve - just don't use more than one modifier class on an element.
Creating a new modifier class to get the combination you want is really cheap.
.commentcolor: $black.comment--admincolor: $green.comment--importantcolor: $yellowfont-weight: bold.comment--admin-importantcolor: $greenfont-weight: bold
<div class="comment comment--admin-important">
Scope state classes to the module to ensure enough specificity
In addition to a single modifier class, you can also put a single state class on an element.
<div class="comment comment--admin is-disabled">
A state class should always override styles from the base module or from any modifier classes. By scoping it to the module...
.commentcolor: $black&.is-disabledcolor: $grey
... you can be sure that it will always have enough specificity to override base module or modifier styles.
As with modifiers, if you find that you need to combine multiple state classes, just make a new one that does exactly what you want instead.
You might also want to specify an element's state in a data attribute rather than with a class. Updating a single attribute's value with JS is much simpler than adding & removing specific classes, and encourages a finite set of discrete states rather than infinite combinations.
<div class="comment comment--admin" data-state="disabled">
.commentcolor: $black&[data-state=disabled]color: $grey&[data-state=loading]background: url(spinner.gif)
What about buttons?
The canonical example we always trot out to demonstrate modifier classes is the humble
<button>. For some reason we just love making buttons infinitely customisable.
<button class="btn btn--warning btn--large btn--round btn--icon">
In my experience at least, buttons are a pretty special case in this regard. We don't really make many other modules that need so many variations.
Breaking the rules for special cases is ok though! I'd even say it's preferable to trying to craft a one-size-fits-all method for different situations.
So you could just use multiple modifier classes & make sure your team knows about the special case.
Or you could use a different naming convention to make combinable modifiers super obvious. Here's one I've been trying out:
<button class="btn -color-red -size-large -shape-round">
.btn&.-color-redcolor: $red&.-color-bluecolor: $blue&.-size-medfont-size: 1em&.-size-largefont-size: 2em// etc etc
There's still a risk of tying yourself to a specific source order, but with some smart naming & awareness that this is a special case you can avoid too much drama.
So yeah. Multiple classes ftw.
Using multiple classes on an element definitely comes with some risks that you don't get with a single standalone class. However the risks mostly disappear if you stick to the "one modifier, one state" rule. Chain away!