htmlcssbem

BEM: how to make sure rules of the right class (parent block) take precedence?


In BEM all selectors (classes) are supposed to have the same specificity. Meanwhile blocks are independent entities and you are supposed to be able to nest blocks in any combination and mix the class definitions of their elements. How do I make parent block rules to have higher priority than the child block rules while following BEM methodology?

Suppose we have two blocks: menu and social. Since blocks are independent sometimes first block will be a part of the second, sometimes the second block will be a part of the first one.

<div class="menu">
  <div class="menu__social social">
      <h1>social content in a menu</h1>
  </div>
</div>

<div class="social">
  <div class="social__menu menu">
      <h1>a menu in social content</h1>
  </div>
</div>

naturally, in such a situation, I need rules of the class menu__social be able to overwrite rules of the class social, so when I have social inside a menu I can style it according to the needs of the parent element.
And wise versa: rules of the class social__menu need to be able to overwrite rules of the class menu, so when menu is a child I can restyle it according to the needs of the parent. How do I do it?

If I simply define rules for each block in a separate file and then merge them together - then rules of the one block come first and are always overwritten by the rules of another block, the order of classes in html is ignored.

/* menu block definition */
.menu {
  background: red;
}

.menu__social {
  background: pink;
}

/* social block definition */
.social {
  background: blue;
}
.social__menu {
  background: green;
}

in this example background will always be blue or green, i.e. every-time I mix menu* with social* the rules of the menu-block are ignored.

http://jsfiddle.net/oq2arsv3/


Solution

  • Let me structure everything I've found out. There are several approaches:

    --------- 1

    First of all, you can reorganize rules between blocks so they don't need to be overwritten in the first place. The main use of mixes is an ability to divide css styles between blocks: external stuff is defined in the parent block and internal in the child block you mix the definitions and you get all element styles needed. BEM just like OOP is based on Single responsibility principle. So if you have one style (color) defined both in parent and child block may be this is the problem on its own and should be changed.

    ------------------ 1.1

    One way to do it is modifiers. Define two different styles of the child block in the child block itself and use them depending on the context (and this is exactly what modifiers are needed for):

    <div class="menu">
      <div class="social social_color_pink">
          <h1>social content in a menu</h1>
      </div>
    </div>
    
    <div class="social">
      <div class="menu menu_color_green">
          <h1>a menu in social content</h1>
      </div>
    </div>
    
    /* menu block definition */
    .menu {
      background: red;
    }
    
    .menu_color_green {
      background: green;
    }
    
    
    /* social block definition */
    .social {
      background: blue;
    }
    
    .social_color_pink {
      background: pink;
    }
    

    ------------------ 1.2

    Another way to do it is what Stefan Bajić suggested - avoid nesting and make a child block a part of the parent block (an element). This will lead to the code duplication, but will allow you to avoid a lot of dependency issues in the future, and, since you can't separate responsibilities of the parent and child block (who defines the color), it is probably the most natural way.

    <div class="menu">
      <div class="menu__social">
          <h1>social content in a menu</h1>
      </div>
    </div>
    
    <div class="social">
      <div class="social__menu">
          <h1>a menu in social content</h1>
      </div>
    </div>
    
    /* menu block definition */
    .menu {
      background: red;
    }
    
    .menu__social {
      background: pink;
    }
    
    
    /* social block definition */
    .social {
      background: blue;
    }
    
    .social__menu {
      background: green;
    }
    

    --------- 2

    You can use context. This is not the best way, since it increases rules specificity and makes HTML less readable, but it is allowed by BEM if nothing else fits.

    <div class="menu">
      <div class="social">
          <h1>social content in a menu</h1>
      </div>
    </div>
    
    <div class="social">
      <div class="menu">
          <h1>a menu in social content</h1>
      </div>
    </div>
    
    /* menu block definition */
    .menu {
      background: red;
    }
    
    .menu .social {
      background: pink;
    }
    
    
    /* social block definition */
    .social {
      background: blue;
    }
    
    .social .menu {
      background: green;
    }
    

    --------- 3

    You can combine all the ways above, so it fits your specific task the most. For example, if social block is huge and menu is small, you can duplicate some code of menu and make menu an element of the social block, and add modifier to the social block and use it in the menu block definition so you don't need to duplicate social code.

    <div class="menu">
      <div class="social_color_pink">
          <h1>social content in a menu</h1>
      </div>
    </div>
    
    <div class="social">
      <div class="social__menu">
          <h1>a menu in social content</h1>
      </div>
    </div>
    
    /* menu block definition */
    .menu {
      background: red;
    }
    
    /* social block definition */
    .social {
      background: blue;
    }
    
    .social__menu {
      background: green;
    }
    
    .social_color_pink {
      background: pink;
    }
    

    Note, that all solutions result in absence of the mixes. This happens just because there is only one css-rule in the example. And thereby, this doesn't mean that you need to get rid of the mixes, you still can and most probably will need to use mixes, but you must avoid overwriting css styles in them, since then the result will depend on the order in which blocks are defined.