When building components, one of the conventions that Frontend developers have agreed last years is the concept of modifier. A modifier is a flag that can change the default component behavior or appearance.

Many CSS methodologies and libraries rely on the base-modifier class approach in order to code those modifiers, but in my experience I have found it has several issues. In my projects during last couple of years I replaced it by a new concept: the component mutations.

In this article I will review the base-modifier class approach and the problems I have faced, and in the second part cover what a mutation is, the rationale behind it, what kind of mutations exist and how to code them.

What is the base-modifier class approach

In the base-modifier class approach the default styling and behavior of a component is contained in a base class, while the modifier is added as an extra class.

The base class would be:

<button class="button">
  Next
</button>

And the modifier, an extra class that modifies the default behavior overwritting properties:

<button class="button button-blue">
  Next
</button>

Let’s see a couple of very well known examples using this approach.

BEM modifiers

BEM is a naming convention that allows developers to build scalable and easy to understand code. BEM stands for Block Element Modifier. In the BEM naming documentation, the definition of modifier is:

Flags on blocks or elements. Use them to change appearance, behavior or state.

Following the previous example, in BEM, a button with a blue color modifier would look like this:

<button class="button button--blue">
  Next
</button>

Twitter Bootstrap

In the documentation, the Bootstrap modifiers are quite well explained as it follows:

Many of Bootstrap’s components are built with a base-modifier class approach. This means the bulk of the styling is contained to a base class (e.g., .btn) while style variations are confined to modifier classes (e.g., .btn-danger).

This is an example of buttons, taken from Bootstrap docs:

<button class="btn btn-primary">
  Primary
</button>

Problematic of base-modifier class approach

After years using this approach, I have found several issues.

Class attributes are unidimensional

The class attribute contains a flat list of elements, all of them at the same level / dimension. Nothing stops the developer from doing, either manually or programatically something like this:

<button class="button button-primary button-secondary">
  Primary
</button>

In this snippet the button has two modifiers for the same purpose: describing the color.

What modifier would apply here, primary or secondary? That would depend on basically two factors: specificity and precedence, both of them with possible side effects.

What would happen if by chance secondary doesn’t reset all the props of primary? In that case, this button would be mixing props of both primary and secondary, resulting in a non controlled state.

Hard to read

Since the attribute class is a flat list of classes, when it contains more than 2 or 3 elements it becomes quite hard to read:

<button class="button button-primary button-big button-full-width button-disabled">
  Primary
</button>

And it can become even harder if the component name is longer than button or if we are applying the modifiers to BEM elements, which by definition have more verbose names.

Prone to global classes

With this approach, it is relatively easy to end up having all those modifiers in global classes, by doing for example in SCSS something like this:

.button {
  /* default props */

  &-primary {
    /* primary modifier props */
  }
}

While in SCSS the previous snippet gives the developer a sense of having scoped the modifier class, when this code compiles to CSS it will result in two global classes.

.button { /* default props */ }
.button-primary { /* primary modifier props */ }

There are ways of solving this in SCSS, but the false sense of scoping has been problematic in my experience.

Element selectors inside modifiers can lead to errors

I have seen this issue quite often, actually. Let’s take an expandable component as example. It contains a button to toggle the content on click. The markup could be something like:

<div class="expandable">
  <button class="expandable__toggler">
    Expand / collapse
  </button>
  <div class="expandable__content">
    This content will expand and collapse on the toggler click
  </div>
</div>

Now, in SCSS if we use & to avoid rewriting the base class name in all the elements, once we are in a modifier the context of the & changes and does not refer to the base class anymore.

.expandable {
  &__toggler {
    /* Compiles to .expandable__toggler */
  }

  &--expanded {
    &__toggler {
      /*
        👻 This will not work since & is now .expandable--expanded, so this selector
        compiles to .expandable--expanded__toggler which does not match our
        toggler element.
      */
    }
  }
}

There is no room for several type of modifiers

Modifier is a very generic term to cover variations, behavior changes or state. Also, coded as class names it does not leave room to clear conventions. I usually like to distinguish between variant and state. While the former is present when the component instance is created, the later is applied after an event or a user action.

Separating those two concepts has helped me to structure better my code and make it more understandable to other developers, but in the base-modifier class approach they both are basically the same.

Component mutations to the rescue

In the second part of this article I speak about what a component mutation, the advantages compared to base-modifier class approach, the types of mutations and how to code them.