CSS: БЭМ классы и наименованиия

Поговорим про технические детали того как обычно работают с БЕМ методологией.

Кот из 5 элемента
Кот из 5 элемента

Ранее мы выяснили как определять, что такое блок, а что такое элемент в методологии. А так же, выяснили, что один и тот же блок со всеми своими элементами может встречаться на странице не один раз. Это и есть технические вводные которые нужны для воплощения методологии в верстке.

Немного вспомним, а какие же вообще есть селекторы ? Чтобы покрыть это требование - " множество блоков на странице " мы можем использовать селекторы по тегам, селекторы по атрибутам и наконец селекторы по классам.

Теги

Рассмотрим сначала селекторы по названию тегов. Эта идея это первое что приходит в голову, она словно витает в воздухе и она похожа, на то как оно должно быть. Многие поколения разработчиков и верстальщиков всячески лоббируют эту идею. В современных реалиях ее вполне можно реализовать так в новых стандартах HTML появились " пользовательские элементы " ( custom elements ).

1
2
3
<my-custom-block>
  <my-custom-block-element></my-custom-block-element>
</my-custom-block>

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

Тогда CSS селекторы будут выглядеть как

1
2
3
4
5
6
my-custom-block {
  background: red;
}
my-custom-block-element {
  padding: 10px;
}

Так же в случае использования селекторов по тегам не очень очевидно, что делать с элементами блоков? Навскидку можно предложить несколько решений.

  1. Использовать название блоков качестве части имени элемента. Тем самым можно создать элементы к которые можно стилизовать исходя из названия их тега, который содержит в себе название элемента. В этом случае на каждый тип такого элемента нужно будет создавать новый пользовательский элемент. Эта техника применена в примере выше.

  2. Использовать Shadow DOM (теневой DOM). Этот способ хорош тем, что обеспечивает хорошую изоляцию стилей. То есть стили элементов внутри блока не будут влиять на страницу целиком, а только на элементы блока внутри теневого дерева, а это именно то что и хотелось добиться. Но и этого подхода есть свои недостатки. Во первых он поддержан не всеми браузерами. Во вторых опять таки как и у пользовательских элементов тут нужно задействовать JavaScript, а это значит что при увеличении разнообразия элементов, опять таки могут возникнуть проблемы с производительностью

Конечно же это не исчерпывающий список способов организации блоков и элементов на странице. Я уверен что существует еще как минимум несколько. Но кажется, чтобы сделать такую организацию html тегами придется задействовать JavaScript. Далее рассмотрим способы которые этого не требуют.

Атрибуты

Как было сказано ранее помимо селекторов по тегам существуют селекторы по атрибутам. Эту особенность тоже можно использовать для организации структуры блоков и элементов. В качестве подопытного атрибута можно использовать rel , так же на просторах интернета, можно встретить использование " data " атрибутов.

Именно тут раскрывается первый недостаток этого метода - он нарушает смысл атрибутов. То есть выходит так, что эти атрибуты задуманы для одного, а по факту их используют для другого. Это приводит к тому, что например поисковые машины ожидают увидеть в rel атрибуте данные для индексации ссылок, а по факту там находиться информация для стилизации этих ссылок. Тоже самое можно сказать и про data атрибуты.

Если же, все это не важно, тогда чтобы организовать систему блоков и элементов, нужно придумать правила наименования, как это было для тегов. Если там была предложена идея “использовать название блоков в качестве части имени элементов” то тут можно ее продолжить.

1
2
3
4
5
<div rel="my-block">
  <div rel="my-block-element">
    <div rel="my-block-header">Заголовок</div>
  </div>
</div>

Как видно на примере не всегда понятно где разделитель “-” выступает в качестве разделителя имени блока от имени элемента, а где он выступает в качестве пробела в имени блока/элемента. Обозначенная проблема будет возникать при выборе любого разделителя, по этому в методологии БЭМ существует договоренность, что в качестве разделителя между именем блока и элемента используется двойное подчеркивание "__" с использованием этого правила пример будет выглядеть как

1
2
3
4
5
<div rel="my-block">
  <div rel="my-block__element">
    <div rel="my-block__header">Заголовок</div>
  </div>
</div>

Используя эту разметку, мы можем определить стили следующим образом

1
2
3
[rel="my-block"] {
  background: red;
}
1
2
3
[rel="my-block__element"] {
  padding: 10px;
}
1
2
3
[rel="my-block__header"] {
  color: blue;
}

Тем самым мы добьемся искомого результата. А что если какой то из наших блоков все же используется свойство rel ?

1
2
3
4
5
6
<div rel="my-block">
  <div rel="my-block__element">
    <div rel="my-block__header">Заголовок</div>
  </div>
  <a rel="nofollow my-block__link">Ссылка</a>
</div>

В этом случае придется воспользоваться селекторами по части атрибута это могут быть " *= “, " ^= “, " ~= “ или ” $= “. Если всегда использовать *” = “ то есть риск ошибиться в названии блоков. Например если у нас есть блоки

1
2
3
<div rel="my-super-block">
  <div rel="super-block"></div>
</div>

При попытке выделить их с помощью селектора *[rel = super-block] будут выделены оба блока, что не всегда ожиданно. Так что лучше в общем случае пользоваться ” ~= “ этот селектор дает меньше всего побочных эффектов.

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

Классы

Если использовать классы для примера который был использован выше для атрибутов, то получится

1
2
3
4
5
6
7
8
<div class="my-block">
  <div class="my-block__element">
    <div class="my-block__header">Заголовок</div>
  </div>
  <a rel="nofollow" class="my-block__link">
    Ссылка
  </a>
</div>

В этом примере сразу заметно отсутствие проблем с ссылками rel=“nofollow” и смыслом - атрибут class придуман специально для работы со стилями отображения элементов.

Так же хотелось бы обратить внимание на пример с вложенными элементами. В своей практике я встречал интересную интерпретацию этого случая ее смысл заключается в том что вложенным элементам в класс дописывался элемент родитель и вместо my-block__header разработчик писал my-block__element__header, так как эти элементы вложены в друг друга. С этим подходом есть несколько опасных моментов

  1. Длина класса элемента начинает расти в зависимости от его глубины вложенности. На 3-4 уровне имя класса становится очень большим, настолько большим, что становится больше основного содержимого страницы.

  2. Самое неприятное в том что такой подход заставляет переделывать стили при изменении иерархии элементов. Ведь изначально идея БЕМ в том что бы упрощать переносы блока и элементов по верстке.

Так что в целом так делать не стоит.

Модификатор

Теперь когда стало понятно что такое блоки и элементы, и почему их нужно реализовывать через классы. Пришло время раскрыть смысл последней буквы в аббревиатуре БЕМ.

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

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

Чтобы использовать модификатор на блоке, принято дублировать название блока и отделять название модификатора от имении блока двойным дефисом “–” или одним подчеркиванием “_”. Тут в отличии от элементов в интернете нет общего мнения. Российская школа говорит об подчеркивании а зарубежная о дефисах. Пример

1
2
3
4
5
6
<div rel="my-block my-block--small">
  <div rel="my-block__element">
    <div rel="my-block__header my-block__header--big">Заголовок</div>
  </div>
  <a rel="nofollow my-block__link">Ссылка</a>
</div>

Где " my-block” - это блок small - модификатор. Тоже самое верно и для элементов my-block__header–big где my-block - блок header - элемент big - модификатор

Буду рад комментариям идеям лайкам, всему тому что поможет сделать эту статью лучше. Поможет вам и другим людям, что прочтут их.