How to structure BEM modifiers with nested elements in SASS

SASS + BEM is pretty much like the sky most of the time, but my common struggle is understanding how best to define BEM modifiers for an element that affects its children when using SASS parent selectors.

I have the following component defined in SASS using BEM style syntax:

.card { background-color: #FFF; &__value { font-size: 2em; color: #000; } } 

This works well due to the SASS parent selector. It keeps the relevant code organized and autonomous.

But when I need to add a modifier that modifies the child using the parent selector, the idea quickly falls apart:

 .card { padding: 2em; &__value { font-size: 1.5em; color: #000; } &--big { padding: 2.5em; &__value { // Is this going to work? font-size: 3em; } } } 

Nope. It generates this:

 .card { padding: 2em; } .card__value { font-size: 1.5em; color: #000; } .card--big { padding: 2.5em; } .card--big__value { // Wrong font-size: 3em; } 

It would be wiser to somehow get this selector:

 .card--big .card__value { font-size: 3em; } 

The reason for this is that you can simply add a modifier to the top-level element and affect any or all of the child elements.

I tried a couple of approaches:

Use two structures

 .card { padding: 2em; &__value { font-size: 1.5em; color: #000; } } .card--big { padding: 2.5em; &__value { font-size: 3em; } } 

This works (especially in this simplified demo), but in a more complex set of components with many modifiers, this can be a potential pain for maintaining and saving errors. In addition, it would be nice to continue using the SASS parent selectors, if possible.

Use a variable for an element

 .card { $component: &; // Set the variable here padding: 2em; &__value { font-size: 1.5em; color: #000; } &--big { padding: 2.5em; #{$component}__value { // Use it here font-size: 3em; } } } 

It works well. But it seems silly to define an element as a variable. Maybe this is the only real way to do this ... I'm not sure. Are there more efficient ways to structure this?

+9
html css sass bem
source share
7 answers

Why not just do it?

 .card { padding: 2em; &__value { font-size: 1.5em; color: #000; } &--big { padding: 2.5em; } &--big &__value { font-size: 3em; } } 
+4
source share

You can separate modifiers in another structure, but nested in a .card selector, for example:

 .card { padding: 2em; &__value { font-size: 1.5em; color: #000; } &--big { padding: 2.5em; } &--big &__value { padding: 2.5em; } } 

This, in turn, will result in the following:

 .card { padding: 2em; } .card__value { font-size: 1.5em; color: #000; } .card--big { padding: 2.5em; } .card--big .card__value { padding: 2.5em; } 

I think this is an almost perfect way, although its not entirely nested. Hope this was of help!

+1
source share

I found a much better way to achieve this with sass, using a variable to store the value of the parent selector, you can use it at any nesting level!

For example, here I save the .card selector in the $ this variable and reuse it as #{$this}

So this code

 .card { padding: 2em; &__value { font-size: 1.5em; color: #000; } $this: &; &--big { padding: 2.5em; #{$this}__value { font-size: 3em; } } } 

will compile in

 .card { padding: 2em; } .card__value { font-size: 1.5em; color: #000; } .card--big { padding: 2.5em; } .card--big .card__value { font-size: 3em; } 

This answer was inspired by this css-tricks article. Thanks Sergey Kovalenko

+1
source share

Your solution looks good, but you can also try @ at-root .

0
source share
 .card { padding: 2em; &__value { font-size: 1.5em; color: #000; } &--big { padding: 2.5em; &__value { // Is this going to work? font-size: 3em; } } } 

You can achieve the result you would like to receive:

 .card { padding: 2em; &__value { font-size: 1.5em; color: #000; } &--big { padding: 2.5em; .card { &__value { // this would work font-size: 3em; } } } } 
0
source share

After going through the same problem, I created a library called Superbem , which is basically a collection of SCSS mixers that will help you write a BEM declarative path. Take a look:

 @include block(card) { padding: 2em; @include element(value) { font-size: 1.5em; color: #000; } @include modifier(big) { padding: 2.5em; @include element(value) { font-size: 3em; } } } 

Gives you:

 .card, .card--big { padding: 2em; } .card__value { font-size: 1.5em; color: #000; } .card--big { padding: 2.5em; } .card--big .card__value { font-size: 3em; } 

Hope you find this helpful!

0
source share

There is another template that you can use here.

This will:
- separate the actual card modifier from the modifiers of its elements
- saves the changed styles in the same element selector so that you do not need to scroll the code up and down to see what changes
- prevent more specific rules from appearing over less specific rules if this is your thing

Here is an example:

 // scss .card { $component: &; padding: 2em; &--big { padding: 2.5em; } &__value { font-size: 1.5em; color: #000; #{$component}--big & { font-size: 3em; } } } /* css */ .card { padding: 2em; } .card--big { padding: 2.5em; } .card__value { font-size: 1.5em; color: #000; } .card--big .card__value { font-size: 3em; } 
0
source share

All Articles