SCSS: Стилизация элементов модифицированного блока

Давайте представим, что у нас есть вот такой простенький HTML-компонент, который мы планируем повторно использовать в нашем приложении. Классы именуем так, как это рекомендует делать методология БЭМ.

<div class="block">
  <h2 class="block__title">Заголовок</h2>
  <p class="block__description">Описание</p>
</div>

Далее стилизуем наш компонент, используя для этого препроцессор SCSS:

.block {
  border: 1px solid red;

  &__title {
    color: black;
  }

  &__description {
    color: blue;
  }
}

Отлично, наш компонент готов. Представим, что где-то в приложении, он должен выглядеть иначе, например, иметь другие цвета у бордера, заголовка и описания.

Так как мы знаем и используем БЭМ, чтобы задать другие стили, идём и добавляем модификатор --secondary для нашего блока:

<div class="block block--secondary">
  <h2 class="block__title">Заголовок</h2>
  <p class="block__description">Описание</p>
</div>

Теперь, когда у нас есть класс-модификатор, необходимо его стилизовать.

Добавляем стили

Стилизовать элементы блока с модификатором в SCSS можно аж несколькими способами. Когда я впервые познакомился с SCSS и столкнулся с такой задачей, первое, что я написал, выглядело как-то так:

.block {
  &--secondary {
    border-color: green;

    &__title {
      color: white;
    }

    &__description {
      color: pink;
    }
  }
}

Конечно же, это не сработало, что вполне логично. Если взглянуть на итоговый CSS, сразу станет ясно почему:

.block--secondary {
  border-color: green;
}

.block--secondary__title {
  color: white;
}

.block--secondary__description {
  color: pink;
}

Одиночный и двойной амперсанды

Наверное, самым простым, но не самым оптимальным решением будет стилизация блока и его элементов отдельно друг от друга с использованием амперсанда:

.block {
  &--secondary {
    border-color: green;
  }

  &--secondary & {
    &__title {
      color: white;
    }

    &__description {
      color: pink;
    }
  }
}

Именно этот подход я использую в своей работе, так как он кажется мне наиболее понятным и лаконичным. Однако, как мы видим, он неидеальный.

Во-первых, нам приходиться отдельно стилизовать сам блок с модификатором и его элементы. Что приводит к дублированию названия модификатора в стилях.

Во-вторых, при таком подходе мы теряем полноценную вложенность и привязку к классу блока.

Переменные

Переменные в SCSS это очень мощный инструмент который позволяет делать какие-то невероятные вещи. В том числе их можно использовать для стилизации элементов модифицированного блока.

$block: block;

.#{$block} {
  .#{$block}--secondary {
    border-color: green;

    .#{$block}__title {
      color: white;
    }

    .#{$block}__description {
      color: pink;
    }
  }
}

Такая запись, в отличие от предыдущего примера, позволяет нам сохранить полноценную вложенность классов в скомпилированном CSS, сравните:

/*С амперсандом*/
.block--secondary .block__description {
  color: pink;
}

/*С переменной*/
.block .block--secondary .block__description {
  color: pink;
}

Однако, этот подход, на мой взгляд, уже не так прост для быстрого восприятия, плюс нам нужно выносить переменную класса отдельно.

Чтобы не выносить переменную, можно написать вот так:

.block {
  $parent: &;

  #{$parent}--secondary {
    color: blue;

    #{$parent}__title {
      color: white;
    }

    #{$parent}__description {
      color: pink;
    }
  }
}

Как точно не нужно делать

Самое неудачное, на мой взгляд, решение — явное указание класса элемента. Если мы захотим изменить класс блока, кроме HTML, менять придётся ещё и стили. В итоге теряется смысл использования препроцессора.

.block {
  &--secondary {
    border-color: green;

    .block__title {
      color: white;
    }

    .block__description {
      color: pink;
    }
  }
}

Источники

  1. https://www.sassmeister.com/
  2. https://sass-lang.com/documentation/
  3. https://ru.bem.info/methodology/key-concepts/