DEV Community

loading...

Dealing with the Scope Issues of BEM

ece profile image Ece Tunca ・4 min read

It is hard to agree on coding conventions. When it comes to CSS, it is usually challenging when you try to implement a comprehensible, clean and reusable code, especially if you are working as a team of developers for a single project. Every developer might have different points of view on these concepts, and with the lack of a CSS naming convention that has been agreed upon, it is inevitable to end up with a messy code, which will turn further development and debugging into a nightmare. Adopting a naming convention will save you hours even if you are the only developer.

BEM (Block Element Modifier) has been one of the most widely used, and at the same time, the most hotly debated naming conventions in the recent years. While BEM provides you with a modular, consistent and systematically written code, it is usually criticised for its naming scheme causing some sort of ugliness. I agree that class names chosen for the sake of specificity can look ugly especially when they are repetitive and used together with grandchild selectors. When combined with SASS, elements and modifiers might create multiple levels of nested components, ending up with complexity and of course, ugliness.

Rather than being the result of the problematic nature of BEM, I believe that these issues usually derive from the wrongly made decisions while determining the block scopes.

Let's say you want to make a simple site header that contains a primary menu and a secondary menu.
Let's code it in an ugly manner:

<header class="header">
    <ul class="header__menu header__menu--primary">
        <li class="header__menu__item">
            <a class="header__menu__item__link" href="#"></a>
        </li>
        <li class="header__menu__item">
            <a class="header__menu__item__link" href="#"></a>
        </li>
        <li class="header__menu__item">
            <a class="header__menu__item__link" href="#"></a>
        </li>
    </ul>
    <ul class="header__menu header__menu--secondary">
        <li class="header__menu__item">
            <a class="header__menu__item__link" href="#"></a>
        </li>
        <li class="header__menu__item">
            <a class="header__menu__item__link" href="#"></a>
        </li>
        <li class="header__menu__item">
            <a class="header__menu__item__link" href="#"></a>
        </li>
    </ul>
</header>
Enter fullscreen mode Exit fullscreen mode

The first problem that catches one's eye is the existence of grandchild selectors .header__menu__item and .header__menu__item__link, which are problematic to use and should be avoided. Here, the issue of determining the block scope comes into play. Does .header__menu have to be an element of the .header block? What about setting it as a free, self-standing block? Maybe we would want to use the same menu somewhere else as well, in the site footer for instance?

<header class="header">
    <ul class="menu menu--primary">
        <li class="menu__item">
            <a class="menu__link" href="#"></a>
        </li>
        <li class="menu__item">
            <a class="menu__link" href="#"></a>
        </li>
        <li class="menu__item">
            <a class="menu__link" href="#"></a>
        </li>
    </ul>
    <ul class="menu menu--secondary">
        <li class="menu__item">
            <a class="menu__link" href="#"></a>
        </li>
        <li class="menu__item">
            <a class="menu__link" href="#"></a>
        </li>
        <li class="menu__item">
            <a class="menu__link" href="#"></a>
        </li>
    </ul>
</header>
Enter fullscreen mode Exit fullscreen mode

As we see, not only has the problem of grandchild selector been solved, but also the class names look shorter and cleaner. More importantly, we have a reusable menu block.

Now let's add some basic styles. The only difference between the primary and the secondary menu will be the width:

.menu {
    display: flex;
    list-style: none;

    &__item {
        margin: 0 1rem;
    }

    &__link {
        text-decoration: none;

        &:hover {
           text-decoration: underline; 
        }
    }

    &--primary {
        width: 50%;
    }

    &--secondary {
        width: 30%;
    }
}
Enter fullscreen mode Exit fullscreen mode

Everything looks alright so far. What if we want to style the menu items differently? For instance, we want to assign different colours for menu items of primary menu and secondary menu. Although modifiers come in handy for additional styles, their usage should be limited to the blocks most of the times. Otherwise we would end up with something like this:

<ul class="menu menu--primary">
     <li class="menu__item menu__item--primary">
         <a class="menu__link" href="#"></a>
     </li>
     <li class="menu__item menu__item--primary">
         <a class="menu__link" href="#"></a>
     </li>
     <li class="menu__item menu__item--primary">
         <a class="menu__link" href="#"></a>
     </li>
 </ul>
Enter fullscreen mode Exit fullscreen mode

Instead of adding modifiers to the .menu__item class, assigning additional styles to the element via the modifier of the block seems like a better approach:

&--primary {
    width: 50%;

    .menu__item {
        color: blue;
    }
}

&--secondary {
    width: 50%;

    .menu__item {
        color: green;
    }
}
Enter fullscreen mode Exit fullscreen mode

While we can use the .menu__item class nested directly inside the modifier as above, using $self will improve your code even more:

.menu {
    $self: &; // first we assign the current scope to $self here
    display: flex;
    list-style: none;

    &__item {
        margin: 0 1rem;
    }

    &__link {
        text-decoration: none;

        &:hover {
           text-decoration: underline; 
        }
    }

    &--primary {
        width: 50%;

        #{$self}__item { // then we pass it here
            color: blue;
        }
    }

    &--secondary {
        width: 30%;

        #{$self}__item {
            color: green;
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

This is just a simple case that shows how the decisions regarding the block scope can effect the general quality, comprehensibility and appearance of the code. As your project gets more complex, initial planning of block scope gains more importance. Class names might still not be very appealing to the eye, but the consistency and predictability they provide will be invaluable to you and your team.

Discussion (0)

pic
Editor guide