Как писать классы по БЭМ?
БЭМ расшифровывается как «Блок Элемент Модификатор». На самом деле, это целый стэк технологий, из которого мы воспользуемся только соглашением по именованию классов.
Почему БЭМ?
- БЭМ позволяет создавать абсолютно независимые блоки. Блоки и элементы получают уникальные имена, так что стили для одного элемента ничего не поломают в другом.
- БЭМ помогает легко придумывать любое количество классов, не повторяющихся между собой.
- БЭМ помогает писать самодокументирующийся код, в классе любого элемента содержится информация о нём.
Подробнее можно почитать в разделах Быстрый старт и Часто задаваемые вопросы на сайте bem.info.
Ниже показаны примеры кода.
-
Простой пример: Блок + Элемент
Допустим, у вас есть блок с заголовком, текстом и кнопкой внутри, например, это всплывающее окно — попап. Разметка:
<div> <h3>Заголовок</h3> <div>Текст</div> <button>Кнопка</button> </div>
Добавляем класс содержащий назначение элемента:
.popup
:<div class="popup"> <h3>Заголовок</h3> <div>Текст</div> <button>Кнопка</button> </div>
Теперь попробуем добавить классы вложенным элементам:
<div class="popup"> <h3 class="title">Заголовок</h3> <div class="text">Текст</div> <button class="button">Кнопка</button> </div>
Классы удобные, но не уникальные. Если на странице будут ещё элементы с классами
.title
и.text
, их стили могут затронуть элементы в попапе. Селектор типа.popup .title
может в будущем создать проблемы со специфичностью. Можно придумать другие классы, но чем больше похожих по смыслу элементов, тем сложнее придумывать новые классы.А теперь применим БЭМ-нотацию: каждому элементу внутри блока добавим префикс с классом родителя, например, для заголовка это будет
popup__title
:<div class="popup"> <h3 class="popup__title">Заголовок</h3> <div class="popup__text">Текст</div> <button class="popup__button">Кнопка</button> </div>
Теперь эти классы легко решают сразу две задачи: во-первых, благодаря уникальным классам стили для них никогда не пересекутся с другими подобными элементами на странице, а во-вторых, по таким классам сразу видно, что это элементы блока
.popup
. -
Пример посложнее: Блок + Элемент + Модификатор
Для примера возьмём сервисное сообщение на сайте. Обычно такие сообщения бывают разных видов, например, сообщение об успешном завершении действия или об ошибке.
<div class="message"> <h3 class="message__title">Заголовок сообщения</h3> <div class="message__text">Текст сообщения</div> </div>
Логично использовать одну и ту же разметку, но с разными цветовыми темами. Именно здесь очень пригодятся модификаторы.
<div class="message message--success"> <h3 class="message__title">Заголовок сообщения</h3> <div class="message__text">Текст сообщения</div> </div> <div class="message message--error"> <h3 class="message__title">Заголовок сообщения</h3> <div class="message__text">Текст сообщения</div> </div>
Обоим элементам можно добавить одинаковые стили используя общий класс
.message
и так же легко можно добавить отдельные стили для каждого из них, используя уникальный класс с модификатором:.message { border: 1px solid gray; } .message--success { border-color: green; } .message--error { border-color: red; }
Оба сообщения будут иметь рамку толщиной один пиксель, но для сообщения об успешной операции она будет зелёной, а для сообщения об ошибке — красной.
-
Ещё сложнее: что делать, если хочется сделать элемент элемента?
Например, на странице есть блок новостей:
<div class="news"> <h3>Новости</h3> <ul> <li><!-- новость --></li> <li><!-- новость --></li> </ul> </div>
Заголовок блока логично получает класс
.news__title
, список —.news__list
, а отдельная новость —.news__item
:<div class="news"> <h3 class="news__title">Новости</h3> <ul class="news__list"> <li class="news__item"><!-- новость --></li> <li class="news__item"><!-- новость --></li> </ul> </div>
Тут никаких проблем возникнуть не должно. Теперь добавим разметку отдельной новости:
<div class="news"> <h3 class="news__title">Новости</h3> <ul class="news__list"> <li class="news__item"> <h4>Заголовок новости</h4> <p>Текст новости</p> </li> <li class="news__item"><!-- новость --></li> </ul> </div>
Нам нужно добавить класс заголовку новости. Первым делом приходит в голову
.news__title
, но такой класс уже занят. Предположим, что второй элемент будет не.title
, а.subject
, тогда в CSS получается такое:.news__title { ... } .news__subject { ... }
Без дополнительных комментариев будет совершенно невозможно понять какой из них является заголовком всего блока, а какой — отдельной новости. Не пойдёт.
Следующий вариант —
.news__item__title
, но в БЭМ нельзя создавать элемент элемента, и это понятно, потому что получается каша. Ещё вариант:.news__item-title
— тоже не годится, потому что может быть неочевидным какtitle
соотносится сitem
. Как же быть?Решение простое: на уровне элемента
.news__item
можно объявить новый блок (например,.news-item
), и строить вложенные классы уже от него. Да, это не самостоятельный переиспользуемый блок, здесь объявление блока нужно только для того, чтобы разгрузить селекторы. Что получается:<div class="news"> <h3 class="news__title">Новости</h3> <ul class="news__list"> <li class="news__item news-item"> <h4 class="news-item__title">Заголовок новости</h4> <p class="news-item__text">Текст новости</p> </li> <li class="news__item"><!-- новость --></li> </ul> </div>
Проблема решена: нам больше не нужно использовать монструозные классы, при этом класс точно описывает элемент, и в CSS будет сразу понятно какой класс за что отвечает:
.news__title { ... } .news-item__title { ... }
Простой и удобный выход из неудобной ситуации.
Больше примеров разметки можно увидеть здесь.
Ещё одно хорошее руководство по использованию БЭМ есть здесь.