8

Can I somehow refactor the following code snippet to get rid of double modifier declaration?

.block {
  &__element {
    rule: value;
  }
  &--modifier {
    rule: value;
  }
  &--modifier & {
    &__element {
      rule: value;
    }
  }
}

Output wanted:

.block {
   property: value;
}
.block--modifier {
  property: value;
}
.block--modifier .block__element {
  property: value;
}
Diéssica
  • 309
  • 1
  • 8
Yan Takushevich
  • 1,843
  • 2
  • 18
  • 23

4 Answers4

13

Nesting elements inside modifiers is a known issue. There are a lot of workarounds.

Variable way

Store the block element in a variable.

And use it interpolated when creating a element inside a modifier.

.block {
  $block: &;

  &__element {
    property: value;
  }

  &--modifier {
    property: value;
    #{$block}__element {
      property: value;
    }
  }
}

See output below.

Function way

1. Create a function that returns the block element.

It'll get the parent selector and cut the word before -- (which is the block). Looks hacky, but it's the simplest way to go.

@function block() {
  $selector: str-slice(inspect(&), 2, -2);
  $index: str-index($selector, '--') - 1;
  @return str-slice($selector, 0, $index);
}

2. Use the function interpolated.

Which will return the name of the block so you don't have to repeat it.

.block {
  property: value;

   &--modifier {
     property: value;
     #{block()}__element {
       property: value;
     }
   }
}

See output below.

Both ways will output to:

.block {
  property: value;
}

.block--modifier {
  property: value;
}

.block--modifier .block__element {
  property: value;
}
Diéssica
  • 309
  • 1
  • 8
  • Thank you. I've also found a wonderful [BEM-constructor](https://github.com/danielguillan/bem-constructor). Unfortunately at the moment it doesn't work with libsass, but it probably will when 3.3 version is released. – Yan Takushevich Aug 20 '15 at 05:35
  • Now to find a solution for when creating elements and modifiers using a mixin. – Djorge Jun 10 '16 at 14:15
  • 1
    BTW, the variable way will not output `.block--modifier .block__element {property: value;}` it will output: `.block--modifier .block--modifier__element {property: value;}` – Seed Jul 21 '16 at 16:32
1

You can place the block within the &--modifier selector like this, using the class name of the block rather than & to target it.

.block {
  &__element {
    rule: value;
  }
  &--modifier {
    rule: value;

    .block {
      &__element {
        rule: value;
      }
    }
  }
}

However, this is possibly not the best BEM solution, you should consider renaming the nested block as an element of the containing block, such as .block__another-element or creating a new block entirely.

Toni Leigh
  • 4,830
  • 3
  • 22
  • 36
1

You could add & alongside the modifier for a solution similar to Toni's.

.block {

  &__element {
    rule: value;
  }

  &--modifier & {
    rule: value;
    
    &__element {
      rule: value;
    }
  }
}

This would however require .block to be a root selector and not nested inside any other selector.

Just another possible solution. For most situations though, I would still prefer Toni's solution.

Mattias Larsson
  • 175
  • 2
  • 6
1

if someone use less, this will help you!

@block:.parent;
@{block}{
    backgroung:red;
    &--modifier{
        backgroung:blue;
    }
    &__child{
        font-size:20px;
        @{block}--modifier & {
           font-size:40px;
        }
    }
}