Handling modifiers in CSS

Most CSS methodologies have a mechanism for handling modifiers to a module/component. BEM has its modifier syntax:

1
2
3
4
5
6
.my-module {}
.my-module--variant {}
.my-other-module {}
.my-other-module__element {}
.my-other-module__element--variant {}

However, this modifier syntax has difficulties when dealing with modules where you need to mix-and-match properties like sizing, colours, borders… Buttons are the common use-case for this, but there can be a lot more use cases: special offer labels, site notifications, even modal windows.

Bootstrap uses btn- modifiers. These classes are declared globally.

1
<a href="#" class="btn btn-default btn-lg active">button</a>
1
2
3
4
5
6
.btn-default {
.button-variant(@btn-default-color; @btn-default-bg; @btn-default-border);
}
.btn-lg {
.button-size(@padding-large-vertical; @padding-large-horizontal; @font-size-large; @line-height-large; @border-radius-large);
}

Here is an example of Foundation’s buttons:

1
<a href="#" class="button alert expand">button</a>

Looking at the HTML, the modifier classes look worryingly global and generically named, but in the SCSS you can see that these classes are in-fact scoped to the module (button, .button) using the &. parent selector.

1
2
3
4
5
6
7
8
9
button, .button {
&.secondary { @include button-style($bg:$secondary-color, $bg-hover:$secondary-button-bg-hover, $border-color:$secondary-button-border-color); }
&.success { @include button-style($bg:$success-color, $bg-hover:$success-button-bg-hover, $border-color:$success-button-border-color); }
&.alert { @include button-style($bg:$alert-color, $bg-hover:$alert-button-bg-hover, $border-color:$alert-button-border-color); }
&.large { @include button-size($padding:$button-lrg); }
&.small { @include button-size($padding:$button-sml); }
&.tiny { @include button-size($padding:$button-tny); }
}

Foundation’s solution is closer to my preferred solution chainable modifiers.

1
<a href="#" class="btn -size-l -color-green">Expanded Button</a>
1
2
3
4
.btn {
&.-size-l {}
&.-color-green {}
}

Similar to foundation’s solution but prepending the classes with a dash to indicate these are chainable classes scoped to the element.

BEM Modifiers Still Useful

Chainable modifiers and Foundations’ solution are very flexible, but for modules where the modifiers involve a number of properties that are coupled it can be awkward to try to use this solution.

Take a carousel module.

Carousel left aligned

And a variant that is positioned to the right.

Carousel right aligned

This version will require a positioning property (e.g. float), the spacing between the carousel items needs to be flipped, and the text alignment needs to change as well.

To accomplish this using chainable modifiers can look like:

1
2
3
4
5
6
7
8
9
10
11
.carousel {
&.-position-right {
float: right
}
&.-spacing-right {
margin: 0 0 5px 10px;
}
&.-text-align-right {
text-align: right;
}
}
1
<ul class="carousel -position-right -spacing-right -text-align-right">...</ul>

Will these modifier classes ever be used for a different variant? For modules like this you can end up with lots of false chainable modifiers that are not used in a mix and match style like the buttons example.

Compared that to a BEM modifier approach:

1
2
3
4
5
6
.carousel--version-right-aligned {
@extend .carousel;
float: right;
margin: 0 0 5px 10px;
text-align: right;
}
1
<ul class="carousel--version-right-aligned">...</ul>

If you do not want mixed syntax in your CSS you can swap the BEM style for the single hyphen style.

1
2
3
4
5
6
7
.carousel {
&.-version-right-aligned {
float: right;
margin: 0 10px 10px 0;
text-align: right;
}
}
1
<ul class="carousel -version-right-aligned">...</ul>

Just Copy & Paste

Finally, if you are having difficulty trying to add a particular modifier without making the module code convoluted, just copy & paste it, making it a new module, and move on.

There is no problem with ‘admitting defeat’ sometimes. The reason the community has had to invent OOCSS, BEM, pre-processors… is that CSS is left wanting when trying to deal with code hierarchies, inheritance, reuse and so we’re just trying to do the best we can with it.

There are likely more important wins than the concern with having some CSS code duplication and increasing your production CSS file size by 0.02 KB.