7

I'm trying to pickup and understand the reasoning behind CSS naming conventions such as BEM or SUITCss. I'm having a hard time understanding the value of descendent class names in the presence of SCSS.

For example:

<ul class="menu">
    <li class="menu__item"></li>
    <li class="menu__item"></li>
    <li class="menu__item">
        <a href="#" class="menu__item_link">link</a>
    </li>
</ul>

.menu {
    .menu__item { 
      //styles
      .menu__item__link { //styles }
    }
    //or alternatively this syntax..
    &__item { //styles }
}

With the ability to nest rules in SCSS, I don't see the compelling reasons for me to include the ancestor class names in my code. Above I have defined styles that should only be used for an "item" that is inside of a "menu", using descendant class names. However, the nested structure already communicates this! The rules for menu__item would only apply to an item under a menu anyway, so why do I need include that in the class name?

Why not:

<ul class="menu">
    <li class="item"></li>
</ul>

.menu {
    .item {//styles}
}

I understand that the descendant naming convention is more explicit and perhaps more future friendly. I argue, however, that it is only more explicit in the html. If I wanted to consult how to build this "menu" module, I could just consult the CSS and see just as clearly how a menu has items nested inside.

I guess one possible advantage is that I could write my css un-nested, like so:

.menu { //styles }
.menu__item { //styles }
.menu__item__link { //styles }

And then use a "menu__item" anywhere and it would still be explicit in the class name that this was the styling of an item under a menu..but then why define it as a descendant of a menu at all then? (Another advantage, I suppose, is shorter CSS identifier strings if things aren't nested)

It seems to me that if a class name is to be used as a descendant under another, then nesting in SCSS achieves this and presents that logic clearly. Why would this BEM syntax be necessary then?

I'd like to hear someone explain the reasoning of this type of convention. I want to adhere to so called best practices, but it's hard for me to do so blindly without fulling understanding a convention.

Jeloi
  • 71
  • 1
  • 3

2 Answers2

8

Several remarks.

1/ First,

.menu {
    .menu__item { /* ... */ }
    //or alternatively this syntax..
    &__item { /* ... */ }
}

The two SCSS syntaxes are not equivalent. The first one uses a cascade (".menu .menu__item"), not the second one (".menu__item"). Only the second one is BEM compliant.

2/ Why not a cascade:

.menu {
    .item { /* styles */ }
}

BEM allows scalability. When we write CSS, we focus only on one small context: the block. And each block can be reused many times.

But cascades are not context-free. In your example, there is a block "menu" that contains an element "item". The context of the element "item" is the block "menu". But the cascade breaks the context separation for the sub-blocks. A prefixed BEM syntax allows nesting blocks, the cascade doesn't. For example:

<ul class="menu">
    <li class="menu__item"></li>
    <li class="menu__item">
        <div class="other-block">
            <span class="other-block__item"></span>
        </div>  
    </li>
</ul>

Notice the element "item" in the sub-block. With a cascade instead of a prefix, it would be styled by a rule that would target the elements "item" of the parent block.

3/ This class name is not BEM compliant:

.menu__item__link { /* styles */ }

Elements don't provide any context. Only blocks provide contexts. The context of a an element is the context of its block. So, "link" is not a descendant of "item" in the BEM tree. The two are brothers, independently of their situations in the DOM tree. You should use:

.menu { /* styles */ }
.menu__item { /* styles */ }
.menu__link { /* styles */ }
Paleo
  • 21,831
  • 4
  • 65
  • 76
3

Lets start with idea of BEM or SMACSS.

The main task any methodology resolves is structuring and modularize your code.

For example BEM use following abstractions:

Block - independent part of UI (like feedback form),

Element - part of block cant exist without block (like feedback form button),

Modificator - helper that let you modify block or element ( like make button bigger or smaller).

SMACSS use different abstractions : Module, Layout, Base, State, Theme. To clearly got the idea, imagine your html page contains from logic layers,

1) BASE - you add css resets, define H1, H2 ... font sizes, define colors. So in base you put things than should be changed.

2) LAYOUT - adds grids, or separate page in regions. 3) Module - independent content item

3) STATE - very close to BEM modificator but connected with some action with module, like is_hided, is_collapsed

4) Theme - may be used for BASE override.

So to separate this abstractions you have to follow some naming convention , so you could from first look understand what does this class do. If you follow naming convention its also much easier to maintain you project6 its easily to explain new team members how code organized and how to write new code that looks like written by one person.

Its extremely important in large scale projects with large teams.

Plus, naming convention helps you to decrease number of descendant selectors, that improve performance.

Evgeniy
  • 2,915
  • 3
  • 21
  • 35
  • The naming convention has no effect on performance unless I define the descendants outside of its ancestor, like in my last example. I guess I understand the argument that something like BEM allows you to "see at first glance what a class does/its context". I guess my argument is that, with nesting in SCSS, the SCSS is just as explicit and clear. – Jeloi Jul 16 '14 at 02:25
  • 2
    as i said following naming convention will help to decrease descendants , yep, like in your last example , about nesting with SCSS, not sure syntax will be the same, but in sass you can use & for building BEM or SMACSS like selectors - http://stackoverflow.com/questions/24383308/sass-change-nested-elements-class-naming-style – Evgeniy Jul 16 '14 at 05:25
  • I like this answer by Evgeniy. I would just add that the BEM naming convention prevents naming collisions where, for example, two blocks have element named `.item`. Sure, you can use direct descendant selectors to prevent this but then it's harder to refactor your markup because any little change will require edits to the CSS. – Gil Birman Jul 19 '14 at 16:01