4

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/

klm123
  • 12,105
  • 14
  • 57
  • 95

5 Answers5

3

Interesting question, not something encountered frequently. The more people use BEM the more they run into uncertainties like this.

Meanwhile blocks are independent entities and you are supposed to be able to mix blocks in any combination.

Blocks are independent entities but I'm not aware that you're able to mix them in any combination (precisely because of this problem which you're running into right now)? Can you provide a source?

Regarding your question, I can see this working out:

.menu {
  background: red;
}

.menu__social {
  @extend .social;
}

.menu__item {
  background: pink;
}
 
.social {
  background: blue;
}

.social__menu {
  @extend .menu;
}

.social__item {
  background: green;
}
<div class="menu">
  <div class="menu__item menu__social">
      <h1>social content in a menu</h1>
  </div>
</div>

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

We had to write more styles but through explicitness and ordering we achieved an overwrite. You can set menu__social to extend social and social__menu to extend menu using Sass @extend.

In a way you're fighting against BEM here, it likes isolation and clearly defined components rather than mixing (and mixing is kinda the spirit of CSS).

Stefan Bajić
  • 374
  • 4
  • 14
  • I did provide the source. If you follow the link "Blocks can be nested in each other. You can have any number of nesting levels." – klm123 Jul 14 '21 at 13:13
  • Yeah, but that's about nesting blocks, not mixing a Block and Element? – Stefan Bajić Jul 14 '21 at 13:16
  • [There's a section in the docs relating to that](https://en.bem.info/methodology/quick-start/#mix) but it's different from what you're asking because there both the Block and Element refer to the same entity (`search-form`) - it's used to overwrite styles in a specific context, not create a unrelated "Block Element hybrid". – Stefan Bajić Jul 14 '21 at 19:24
  • I've read that site 2-8 times already. And this link is one of the reasons I ask all these questions. I'm sorry for being not precise in my words before. We don't mix blocks, we mix classes, which define blocks/elements. And we nest blocks. That said when you nest blocks all their elements can be mixed (theoretically). And since blocks are in-depended we need to be able to handle both social as a child block of menu and menu as a child block of social. What you propose in this answer is basically to avoid nesting blocks alltogether and copy the code. Which could be a way,it's just not DRY way. – klm123 Jul 14 '21 at 20:11
2

This is where Modifier syntax comes in handy. Since your styles can be one or the other and you need to be able to establish which takes precedence, then I think that using modifiers to change that behavior when needed is true to the spirit of BEM.

Using your fiddle example, we can use a combination of modifiers and CSS specificity to help provide the conventions needed to override the established defaults:

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

.menu__item, .menu__item.menu__item--social {
  background: pink;
}

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

So now if we look at your original markup, here, you'll see that the default behavior remains unchanged:

<div class="menu">
  <div class="menu__item social">
      <!-- This is blue -->
      <h1>social content in a menu</h1>
  </div>
</div>

<div class="social">
  <div class="social__item menu">
      <!-- This is green -->
      <h1>a menu in social content</h1>
  </div>
</div>

However, if we now use our modifiers to override this behavior when desired, then we will see different results:

<div class="menu">
  <div class="menu__item menu__item--social social">
      <!-- This is pink -->
      <h1>social content in a menu</h1>
  </div>
</div>

<div class="social">
  <div class="social__item social__item--menu menu">
      <!-- This is red -->
      <h1>a menu in social content</h1>
  </div>
</div>
maiorano84
  • 11,574
  • 3
  • 35
  • 48
  • BEM clearly states that combined selectors are not recommended (read "not allowed with strict BEM") https://en.bem.info/methodology/css/#selectors. What you do here is basically increase the specificity of block elements, which can be achieved more directly by repeating the element class two times in the selector: ".menu__item.menu__item". The reason it is not ideal solution is that once you do it you can't use block modifiers anymore. "social_mod" won't be able to overwrite "menu__item" rules. – klm123 Jul 14 '21 at 09:45
  • Also the name "menu__item--social" contradicts to modifier naming convention "https://en.bem.info/methodology/quick-start/#modifier" and kinda the whole point of modifiers, which should refer to appearance (how it looks), not the essence (what its purpose). One can "solve" this problem by renaming "menu__item--social" to ""menu__item--pink", but than html won't be readable anymore " – klm123 Jul 14 '21 at 09:51
-1

If I understood correctly, would you want something like that?

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

    .menu .menu__item {
      background: pink;
      display:inline-block;
    }

    /* social block definition */
    .social {
      background: blue;
      padding:15px;
    }
    .social__item {
      background: green;
      display:inline-block;
    }
    <div class="menu">
      <div class="menu__item social">
          <h1>social content in a menu</h1>
      </div>
    </div>

    <div class="social">
      <div class="social__item menu">
          <h1>a menu in social content</h1>
      </div>
    </div>
  • 1
    So you simply increase specificity of menu__item rule? Why does it fit to BEM? Also why do you don't increase specificity of the social block? Blocks are independed, thereby the same methodology should be applied to all of them, right? – klm123 Jul 14 '21 at 10:42
-1

order matters in css for example if I add .menu after .menu__social then .menu` will get applied (if they target the same css property) for more information check out this video by kevin powell linked here

haider
  • 74
  • 1
  • 5
-1

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.

klm123
  • 12,105
  • 14
  • 57
  • 95