Skip to content

Instantly share code, notes, and snippets.

@jonathantneal
Last active July 30, 2021 02:48
Show Gist options
  • Save jonathantneal/cba4102188d30f766b18 to your computer and use it in GitHub Desktop.
Save jonathantneal/cba4102188d30f766b18 to your computer and use it in GitHub Desktop.
My journey into the Weightless Nesting of BEM

My journey into the Weightless Nesting of BEM

Inspired or reignited by BEM and Sass 3.4, I’ve been exploring new ways to write clean, object oriented CSS.

If you have not already introduced yourselves to either Sass or BEM, I highly recommend reading getting your head ’round BEM syntax and Sass 3.4 is Out!.

I love the idea of object oriented CSS. It’s all about modular, longer-lasting code that’s easier to edit. It promises less headaches when I come back to the code days, weeks, or months later. Still, two issues primarily haunt me, and I’ll be frank about them:

Frank

  • I think the BEM syntax is ugly looking and hard to use in an IDE.
  • I think the implementation of real-life BEM is too messy.

So, join me as I set out to resolve or at least work around these issues.

Let’s choose a syntax

Creating BEM-like objects in CSS typically looks like this:

.block {}

.block__some-element {}

.block--modifier {}

Here, two underscores __ are used to separate a block from an element, two dashes -- are used to separate a block from its modifier, and a dash - is used to distinguish words in a long name.

Ugh, underscores

Tabs vs Spaces

This is so not the battle between tabs and spaces. Almost immediately, I have two problems with underscores:

Problem #1: Underscores are never considered word separators, making code traversal difficult.

There I am, navigating across code with my keyboard, and I keep skipping over BEM elements because the underscore character is not considered a separator. To resolve this, I edited my User Settings in Sublime Text to add the underscore character _ as a word separator.

{
    "word_separators": "./\\()\"'-_:,.;<>~!@#$%^&*|+=[]{}`~?"
}

Believe me, it’s in there!

Anyway, at first, this made BEM much more workable for me. Suddenly I was stopping at all the right places and my IDE was BEM-happy. Unfortunately I discovered shortly thereafter that all of the other code in every single project was suddenly much less workable, as the underscore had often been used to distinguish between words that were intended to be selected or skipped over together just the same as a single word. After an “Undo, undo!” I was back to square one.

Problem #2: The mixed use and doubling of underscores and dashes is difficult to read.

I need glasses

Not at first, but as you start to build things the BEM way, those class names start looking long and foreign next to the other HTML.

<div class="my-block my-block--modifier">
    <a class="my-block__some-element" data-item="5">Item 5</a>
</div>

I figure this will happen with any object-like class name system, but I wonder if BEM would be better suited for HTML-preprocessed environments like Slim.

.my-block.my-block--modifier
    a.my-block__some-element[data-item="5"] Item 5

Or did that just make it worse? Maybe I just need to focus on this underscore issue.

An alternative syntax

I want something similar to BEM, but with dash separators. Okay, how about:

.block {}

.block--some_element {}

.block-modifier {}

Here, two dashes -- are used to separate the block from the element, one dashes - is used to separate the block from the modifier, and an underscore _ is used to distinguish words in a longer name. While not without flaw, this is my preferred method.

What bothers me about this method is how similar elements and modifiers look. One dash means “Change is coming!” but a second dash means “And by change I meant a child!” While that’s true in real life, it doesn’t translate so clearly to me in markup.

Implementation

So now if I know how I’m going to separate blocks, elements, and modifiers, how am I going to actually assemble and populate them in my stylesheet? I see a few ways to do this.

Method #1: Full litany

What is a litany

This method involves a lot of repetition, which means it’s prone to human error, but on the plus side it will work in plain CSS.

.block {
    background: black;
}

.block-element {
    background: red;
}

.block--modifier {
    background: white;
}

.block--modifier .block-element {
    background: blue;
}

Pros: This works in plain CSS. Each line is very clear. This is totally searchable, so I can copy and search for something I found in HTML or my browser inspector.

Cons: This is going to get very meticulous and requires me to very careful with names and dashes. This will be hard to update if and when I rename any block, element, or modifier. This doesn’t make the relationship between blocks, elements, and modifiers very clear.

Let’s get Sassy.

Method #1: Selector reference

The Sass ampersand

Using the Sass ampersand & as a reference to the parent selector, writing BEM-like CSS gets significantly easier.

.block {
    background: black;

    &-element {
        background: red;
    }

    &--modifier {
        background: white;

        .block-element {
            background: blue;
        }
    }
}

Pros: This is extremely small. This has very little name repetition. This looks almost like plain CSS. This makes makes the relationship between blocks, elements, and modifiers pretty clear.

Cons: This requires Sass. This doesn’t work cleanly when modifiers get involved. This is not totally searchable, because I’ll need to understand the relationship between the element and its block to imagine the full class name, which will get even more difficult to skim once I add a realistic number of properties and elements. Oh, and this still requires me to be careful with dashes.

An aside regarding modifiers

Modifiers trouble me in general. Elements wrapped in a block with a modifier get extra weight, e.g. .block--modifier .block-element whereas elements not wrapped by a block with a modifier are weightless, e.g. .block-element.

Method #3: BEM mixins

Oh mixins

When I use Sass-BEM mixins to identify components, things get wonderfully clear and intentional. While not without flaw, this is my preferred method.

@include b(block) {
    background: black;

    @include e(element) {
        background: red;
    }

    @include m(modifier) {
        background: white;

        @include e(element) {
            background: blue;
        }
    }
}

Pros: This is small. This has no name repetition. This makes the relationship between blocks, elements, and modifiers wonderfully clear. This works great with modifiers.

Cons: This is starting to look like something not-CSS. This requires a lot of mixins. This is not easily searchable if I’m copying a rendered class from the browser inspector.

This last implementation doesn’t require me to inline my separators either, so you could use it with the traditional BEM syntax just as easily as I use it with my own version. That’s a plus. I only wish there were a way to clear out the @include, @include, @include bossing around my stylesheet.


While I haven’t totally solved my problem, I think I’m in a much better place to write modular CSS that works well for me, lasts a long time, and is still easy to come back to.

Imgur joke

@davideforestali
Copy link

I still have a doubt: how I am suppose to behave when it comes to lay down a grid system?

I'm going to use a grid system on css side instead of dom-littering .row and .col classes (see this article https://zellwk.com/blog/from-html-grids-to-css-grids/) so I'd use bootstrap 4 @extend %container, %row and %cols which are also flexbox based.

So the question is, how am I supposed to name my containers, rows and columns with BEM naming conventions? they tried to explain it here as well https://zellwk.com/blog/css-architecture-1/ but the examples cases here are too simple and can't be compared with some real-life cases.
Should I still use .container and .row classes mixed with block classes or should I go with something like .block__container, .block__row, .block__col ??

thanks very much for your help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment