BEM Mixins

The best introduction to BEM is from Harry Roberts:

BEM – meaning block, element, modifier – is a front-end naming methodology thought up by the guys at Yandex. It is a smart way of naming your CSS classes to give them more transparency and meaning to other developers. They are far more strict and informative, which makes the BEM naming convention ideal for teams of developers on larger projects that might last a while.

Since Sass 3.3, we can write things like this:

.block {
    /* CSS declarations for `.block` */

    &__element {
        /* CSS declarations for `.block__element` */
    }

    &--modifier {
        /* CSS declarations for `.block--modifier` */

        &__element {
            /* CSS declarations for `.block--modifier__element` */
        }
    }
}

As long as CSS rules are short and base selector is simple, readability remains okay. But when things get more complex, this syntax makes it hard to figure out what's going on. Because of this, we might be tempted to build two wrapper mixins for our BEM syntax:

/// Block Element
/// @access public
/// @param {String} $element - Element's name
@mixin element($element) {
    &__#{$element} {
        @content;
    }
}

/// Block Modifier
/// @access public
/// @param {String} $modifier - Modifier's name
@mixin modifier($modifier) {
    &--#{$modifier} {
        @content;
    }
}

Rewriting our previous example with our brand new mixins:

.block {
    /* CSS declarations for `.block` */

    @include element('element') {
        /* CSS declarations for `.block__element` */
    }

    @include modifier('modifier') {
        /* CSS declarations for `.block--modifier` */

        @include element('element') {
            /* CSS declarations for `.block--modifier__element` */
        }
    }
}

Note that quotes around strings are optional, we only add them for extra readability.

Now, if you feel like element and modifier are too long to type, you can create two shorter aliases like so:

/// @alias element
@mixin e($element) {
    @include element($element)  {
        @content;
    }
}

/// @alias modifier
@mixin m($modifier) {
    @include modifier($modifier) {
        @content;
    }
}

Using aliases:

.block {
    /* CSS declarations for `.block` */

    @include e('element') {
        /* CSS declarations for `.block__element` */
    }

    @include m('modifier') {
        /* CSS declarations for `.block--modifier` */

        @include e('element') {
            /* CSS declarations for `.block--modifier__element` */
        }
    }
}

Comments

  1. User Avatar
    Adam Taylor
    Permalink to comment#

    Wouldn’t the second &__element produce .block–modifier__element?

  2. User Avatar
    Stol
    Permalink to comment#

    Wrong. Tuto. 1st reply is right. modifier is always in last position.

    And usage of mixins gives unnecessary complexity.

  3. User Avatar
    luis
    Permalink to comment#

    I don’t understand why we have to over complicate Sass code. This makes newbies want to run. The default way is shorter and very simple to understand…

    .block {
    color: black;

    &__element { border: 1px solid; }
    }

    • User Avatar
      Hugo Giraudel
      Permalink to comment#

      &__element might be simpler to understand for you, but for newbies like you said, such syntax might be unfriendly. Let people choose their own tools.

    • User Avatar
      petrov

      totally agree with you, introducing mixins makes it less readable

    • User Avatar
      Will
      Permalink to comment#

      100% agree with luis. This is not solving a problem, in-fact it’s complicating things. The time it would take a ‘newbie’ to learn what this mixin does they could have figured out what &__element does. I mean they still have to add the class to the HTML right?!?

    • User Avatar
      Reuven Karasik
      Permalink to comment#

      I disagree with luis. I do believe that newbies should learn how to use &__ before those mixins, just like they should learn CSS before learning SASS/LESS.
      I think that those mixins come in handy when you’re not new anymore, and you want to make your code more semantic and to make more sense – and enable your brain to understand quicker what each line means with simple English.

      Plus, when using SASS syntax, those mixins are very short and quick to use:

      .search-form
          +e(input)
              background-color: grey;
      
  4. User Avatar
    James
    Permalink to comment#
    @include e('element') {
      /* CSS declarations for `.block__element` */
    }
    

    This to me would seem overly more complex as opposed to declaring an ampersand for a “newbie”. How is a “newbie” expected to know how to write mixins and includes as opposed to simply figuring out that they can pass an ampersand for nesting selectors?

    @include m('modifier') { ... } is still more costly (and unnecessary) fluff as opposed to simply writing out a simple declaration. This, if anything, encourages people, or “newbies” to use costly manoeuvres to start nesting everything. Pretty much contrary to the article seen here.

    Could be overseeing this as actually “useful”, but I’m open to opinions! :-)

    Cheers.

    • User Avatar
      James
      Permalink to comment#

      … and, a now even more unsearchable code base, which brings me back to how useful comments are when nesting selectors — again, contrary to the above article reference. Hmm. I need convincing! Haha.

  5. User Avatar
    Pierre
    Permalink to comment#

    Hey guys, did you notice that they’re now using a single underscore “_” instead of double dashes “–” for modifiers?
    http://en.bem.info/method/definitions/
    Actually, I’m not against this even if I find it a bit harder to read, but if anyone of you knows why they made this choice, I would be happy to know.

  6. User Avatar
    Jeferson Calazans
    Permalink to comment#
    .search {
      ...
    
      @include element(input),
      @include element(button) {
        ...
      }
    }
    

    I tried to do this, but I got this error:

    Invalid CSS after "... element(input)": expected "}", was ","

    • User Avatar
      Jeferson
      Permalink to comment#

      I use this to solve my problem.

      %base {
        ...
      }
      
      .search {
        ...
      
        @include element(input) {
          @extend %base;
          ...
        }
        @include element(button) {
          @extend %base;
          ...
        }
      }
      
  7. User Avatar
    nicodev3
    Permalink to comment#

    Could the mixin accept multiple parameters for styling more than one element ?

    @include e(element1, element2) {
      ...
    }
    
    // output :
    
    .element1, .element2 {
      ...
    }
    
  8. User Avatar
    Hillius
    Permalink to comment#

    I have my own way of structuring my SCSS and so far others I work with find it easy to understand/expand/maintain…but I have been looking at stuff like OOCSS, SMACSS, BEM etc.

    It seems to me that this approach falls apart in a situation such as…

    (Assuming Jade for markup)

    .button
      .button__icon
      .button__text Text
    
    .button.button--disabled
      .button__icon
      .button__text Text
    

    Let’s say the --disabled class is applied by ng-class. If I want my icon/text to have different properties based on the button being --disabled, I would need to do…

    .button.button--disabled
      .button__icon.button--disabled__icon
      .button__text.button--disabled__text Text
    

    Seems like more of an annoyance, or am I thinking of the wrong use-case here?

    • User Avatar
      Jason T Featheringham
      Permalink to comment#

      Good question. I generally solve this using an attribute selector like so:

      . button {
        .button__icon { ... }
        .button__text { ... }
      }
      
      // extend all .button--{whatever} 
      [class^="button--"] {
        @extend .button;
      }
      

      Unfortunately, this will not work for HTML that has multiple classes (like .btn.primary), but I generally avoid that anyway, using one class to represent the thing, and Sass to determine the presentation layers.

    • User Avatar
      Andrei Popa
      Permalink to comment#

      Following BEM conventions, you don’t need to specify the block modifier within the element. So .button--disabled__icon would not be considered valid BEM code.

      Instead, your nesting should look like this

      .button.button--disabled
        .button__icon
        .button__text
      

      And for this to work, the structure of the compiled CSS would be

      .button {
      }
      
      .button__icon {
      }
      
      .button--disabled {
      }
      
      .button--disabled .button__icon {
      }
      

      This output can be achieved by updating the “unnecessary” mixins and make them aware of this use case. Now the mixins would actually become useful.

      With Sass 3.4, string functions can be performed on the & selector.

      This is very exciting, since within the modifier() and element mixins we would be able to
      1. create a function that checks if the parent selector has a modifier already
      2. compute the class names accordingly

      To detail, if we have this type of input

      @include block('button') {
        @include element('icon') {
        }
      
        @include element('text') {
        }
      
        @include modifier('disabled') {
          @include element('icon') {
          }
      
          @include element('text') {
          }
        }
      }
      

      The element('icon') mixin would check if the parent selector (&) contains a modifier already and output .block--modifier .block__element, otherwise just output block__element.

  9. User Avatar
    Danny Hadley
    Permalink to comment#

    I love the use of these mixins for generating BEM code but

    +element("breadcrumbs)
      +clearfix
      display: block
    
      +element("item")
        float: left
    
      +element("link")
        display: inline-block
        text-decoration: underline
    
        &:after
          content: '>'
    

    How would I go about getting rid of the last link’s :after?

    I can’t:

      +element("item")
        float: left
    
        &:last-child
    
          +element("link")
            &:after
              display: block
    
    • User Avatar
      Andrei Popa
      Permalink to comment#

      The idea behind the BEM methodology is to strip any logic into small pieces, so everything becomes a lot more maintainable. I would assume the ‘breadcrumb’ in your code is a block, while item and link are elements.

      Indeed, nesting an element inside an element shouldn’t work (because it breaks the very basic BEM rules). But you could make it work by turning the item into a block and build a block-element-modifier object out of it.

      So you would keep the current structure up to the item, then inside create a breadcrumb-item block, where you define your advanced link logic

      +b('breadcrumb')
        + e('item') 
          float: left
      
      +b('breadcrumb-item')
        +e('link')
          color: blue
          text-decoration: underline
      
        // this below acts like a modifier for the block now
        // and we define the link element in the context of this modifier,
        // which doesn't break any rules
      
        &:not(:last-child)
          +e('link')
            &:after
              content: '>'
      

      Then in the html you could use this new BEM block to complement the logic of the initial element

      .breadcrumb
        .breadcrumb__item.breadbrumb-item
          .breadbrumb-item__link
      
        .breadcrumb__item.breadbrumb-item
          .breadbrumb-item__link
      
        .breadcrumb__item.breadbrumb-item
          .breadbrumb-item__link
      

      How is this helpful? You’ve now separated the logic into a smaller BEM module and split responsibilities as well.
      The item element .breadcrumb__item handles the float, the item block .breadcrumb-item handles the link and the separator logic.

      If you’ll add more complexity to the link, like an icon or a highlighted modifier, you’re going to do it a lot easier by changing only this small structure.

  10. User Avatar
    Danny Hadley
    Permalink to comment#

    I dont think the

      &:not(:last-child)
        +e('link')
          &:after
            content: '>'
    

    will work though – the e mixin is going to try to add it’s __link selector on top of the &:not(:last-child) which would result in something like this:

    .breadcrumb-item:not(:last-child)__item {
      ...
    }
    

    see this gist. Unless my e mixin is incorrect.

    • User Avatar
      Danny Hadley
      Permalink to comment#

      Actually, I just figured out a workaround – see this gist. The key was to take the & parent selector on the e mixin and scan it for psuedo selectors. if they existed, slice them out.

  11. User Avatar
    Andrei Popa
    Permalink to comment#

    That’s right Danny, the element mixin should be a bit more complex than the one provided in the example. It’s not enough to just checking if the selector contains a pseudo-selector, you also need to consider if the parent contains a modifier or not, to generate classes like .block--modifier .element, instead of .block--modifier__element – which is not valid BEM.

    Your gist should be working just fine.

    I’m using a set of custom mixins that cover more use cases, maybe I can write a short introduction and post the link here, if you think it could be helpful.

    • User Avatar
      Danny Hadley
      Permalink to comment#

      Good call! Yeah I’d like to see what you’ve come up with to get to the block/element from a parent selector. I opened this issue on a library that looks promising. It would be nice to see a community driven standalone library of BEM-related mixins.

  12. User Avatar
    Andrei Popa
    Permalink to comment#

    Here’s a blog post describing some more use cases and a set of advanced mixins that help create modular and maintainable BEM structures.

    https://m.alphasights.com/how-we-use-bem-to-modularise-our-css-82a0c39463b0#.o4tc7cevq

  13. User Avatar
    gagan
    Permalink to comment#

    sderhbr rfvjui o

  14. User Avatar
    Bram
    Permalink to comment#

    I still think it’s too bad that the starts-with (wildcard) selector doesn’t work with nested ampersands (and yes, I do understand the logic why this doesn’t work, just sayin’). This would eliminate the need for a double block selector. So instead of using <div class="tile tile--disabled"> it would be possible to just write <div class="tile--disabled"> while still being able to style the tile and a disabled version in the same 1 level deep nesting block. This would result in losing 1 level of specificity in scss (which could be quite useful):

    [class=^="tile--"] {
        width: 50px;
        height: 50px;
        background-color:  $button-background-color;
    
        &disabled {
            background-color: $button-disabled-background-color;
        }
    }
    
  15. User Avatar
    Andrei Popa
    Permalink to comment#

    This is an interesting idea indeed, you can create a mixin that could receive the wildcard match class name and return the wildcard + modifier (haven’t given it too much thought, but it’s doable, I’m sure).

    Another thing you could do if you really hate the standard BEM convention is to have the block properties in a placeholder, then extend those on all modifiers.

    It might look like this

    %_title {
      width: 50px;
      height: 50px;
      background-color:  $button-background-color;
    }
    
    .title {
      @extend %_title;
    
      &--disabled {
        @extend %_title;
    
        background-color: $button-disabled-background-color;
      }
    
      &--inverted {
        @extend %_title;
    
        color: $color-white;
        background-color: $color-black;
      }
    }
    

    This is one way to do it without having to duplicate the generated CSS properties.

    But the whole point of the modifier is to extend styles of an element or a block. I find it really useful to call class="
    block__title
    block__title--highlighted
    block__title--with-icon

    but in the end it’s just a matter of conventions.

  16. User Avatar
    Florian Reuschel
    Permalink to comment#

    Very nice. Your approach inspired me to attack a readability problem I’ve always had with Sass + BEM: combining multiple modifiers.

    I extended your mixin to take multiple parameters:

    /// Block Modifier
    /// @access public
    /// @param {String[]} $modifiers - Modifiers' names
    @mixin modifier($modifiers...) {
        // Create a string of additional modifiers
        $additionalModifiers: '';
        @if length($modifiers) > 1 {
            @for $i from 2 through length($modifiers) {
                $additionalModifiers: $additionalModifiers + '#{&}--' + nth($modifiers, $i);
            }
        }
    
        // We need to set the first modifier "by hand" to avoid nesting
        &--#{nth($modifiers, 1)}#{$additionalModifiers} {
            @content;
        }
    }
    

    This makes Sass code like this…

    .alert {
        color: black;
    
        @include modifier(inverted) {
            color: white;
            background-color: black;
        }
    
        @include modifier(warning) {
            color: yellow;
        }
    
        @include modifier(warning, inverted) {
            color: black;
            background-color: yellow;
        }
    }
    

    …generate the following CSS:

    .alert {
        color: black;
    }
    .alert--inverted {
        color: white;
        background-color: black;
    }
    .alert--warning {
        color: yellow;
    }
    .alert--warning.alert--inverted {
        color: black;
        background-color: yellow;
    }
    

Submit a Comment

Posting Code

You may write comments in Markdown. This makes code easy to post, as you can write inline code like `<div>this</div>` or multiline blocks of code in triple backtick fences (```) with double new lines before and after.

Code of Conduct

Absolutely anyone is welcome to submit a comment here. But not all comments will be posted. Think of it like writing a letter to the editor. All submitted comments will be read, but not all published. Published comments will be on-topic, helpful, and further the discussion or debate.

Want to tell us something privately?

Feel free to use our contact form. That's a great place to let us know about typos or anything off-topic.

icon-anchoricon-closeicon-emailicon-linkicon-logo-staricon-menuicon-nav-guideicon-searchicon-staricon-tag