БЭМ для начинающих. Очевидные и неочевидные вопросы верстки

Эта статья написана по мотивам БЭМапа — митапа по БЭМ — с одноименным названием БЭМ для начинающих.

Нас часто спрашивают о верстке по БЭМ и про технологии, которые мы используем: почему нужно делать так, а не иначе? Зачем мы создали целый стек новых технологий, когда можно пользоваться готовыми? Зачем придумали такие длинные имена в классах?

На БЭМапе Владимир Гриненко дал ответы на эти вопросы. Мы решили сохранить их в статье.

Немного теории и практики:

  • Основные почему
  • Основы БЭМ
  • Модификаторы и миксы
  • Удобство параллельной разработки и верстка блоками
  • Блоки в файловой структуре
  • Шаблоны в БЭМ
  • Тестирование верстки
  • Сборка проекта
  • С чего начинать?

Зачем вам БЭМ

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

Основные почему

  • Почему не используем ID?
  • Почему не используем селекторы тега?
  • Почему не используем универсальный селектор?
  • Почему не используем общий сброс стилей?
  • Почему не используем вложенные селекторы?
  • Почему не совмещаем тег и класс в селекторе?
  • Почему не используем комбинированные селекторы?
  • Почему не используем селекторы атрибутов?

Не используем идентификаторы (ID селекторы)

ID определяет уникальное имя HTML-элемента. Если имя уникально, второй раз в интерфейсе его использовать не получится. Это мешает повторно использовать код.

Частые заблуждения

1. ID обязателен для работы с JavaScript.

На практике современным браузерам не важно, с какими данными работать: ID или классами. Браузер одинаково быстро обрабатывает любой селектор.

2. ID используют с тегом <label>.

Если поместить <label> в контрол, ID становится не нужен. Вместо <input id="идентификатор"><label for="идентификатор">Текст</label> можно написать так: <label><input type="...">Текст</label> .

Не используем селекторы тега

HTML-разметка страниц нестабильна: новый дизайн сайта может поменять вложенность разделов, уровень заголовоков (например, с <h1> на <h3>) или превратить абзац <p> в тег <div>. Любые подобные изменения сломают стили, написанные на теги.

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

Расширенный набор семантических тегов также не может выразить все потребности верстки.

Например: в шапке страницы расположен логотип, по клику на который открывается главная страница сайта (index).

Напишем разметку с помощью тегов: для картинки используем тег <img>, для ссылки — тег <a>.

<header>
<a href="/">
<img src="img.logo.png" alt="Лого">
</a>
</header>

Чтобы отличать ссылку с логотипа от обычной ссылки в тексте, понадобятся дополнительные стили. Отменим подчеркивание и синий цвет для ссылки в логотипе:

header a {
...
}

На главной странице ссылка в логотипе не нужна, поэтому изменим разметку для index-страницы:

<header>
<!-- тег <a> заменен на <span> -->
<span>
<img src="img.logo.png" alt="Лого">
</span>
</header>

Для тега <span> отменять подчеркивание и синий цвет не нужно. Поэтому вынесем общие правила для ссылки в логотипе с разных страниц:

header a,
header span
{
...
}

На первый взгляд, такой код имеет право на существование. Но представьте ситуацию, в которой дизайнер убирает логотип из макета. Такие имена селекторов не помогут понять, какие стили удалять из проекта вместе с логотипом. Селектор header a не показывает связь ссылки и логотипа. Такой селектор может принадлежать ссылке в меню шапки или, например, ссылке на профиль автора, а селектор header span может относиться вообще к любой части шапки.

Чтобы избежать путаницы, достаточно записать стили для логотипа с помощью селектора класса с именем logo:

.logo {
...
}

Не используем общий сброс стилей (reset)

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

В БЭМ не принято использовать «reset» и «normalize» даже для отдельно взятого блока.

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

Не используем универсальный селектор (*)

Универсальный селектор сообщает, что в проекте создан стиль, который влияет на все узлы в верстке. Это накладывает ограничения на повторное использование верстки в другом проекте:

  • Необходимо дополнительно переносить в проект стили под «звездочкой». Но в таком случае универсальный селектор может повлиять на стили в новом проекте.
  • Необходимо добавлять стили со «звездочкой» в переносимую верстку.

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

Общие стили не выигрывают время: часто разработчик начинает с того, что сбрасывает все отступы для универсальных компонентов (* { margin: 0; padding: 0; }), а потом все равно задает их как в макете (например, margin: 12px; padding: 30px;).

Не используем вложенные селекторы

Вложенные селекторы увеличивают связность кода и затрудняют его повторное использование.

БЭМ не запрещает вложенные селекторы, но рекомендует не злоупотреблять ими.

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

.button_hovered .button__text
{
text-decoration: underline;
}
.button_theme_islands .button__text
{
line-height: 1.5;
}

Не используем комбинированные селекторы

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

Рассмотрим такой код:

<button class="button button_theme_islands">...</button>

Зададим CSS-правила в селекторе .button.button_theme_islands ради экономии в записи.

Теперь добавим блоку модификатор active:

<button class="button button_theme_islands button_active">...</button>

Селектор .button_active не переопределит свойства блока, записанные как .button.button_theme_islands, так как специфичность .button.button_theme_islands выше, чем у .button_active. Для успешного переопределения селектор модификатора блока также должен быть скомбинирован с селектором .button и объявлен ниже .button.button_theme_islands, так как специфичность обоих селекторов одинакова:

.button.button_theme_islands {}
.button.button_active {}

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

.button_theme_islands {}
.button_active {}
.button {}

Не совмещаем тег и класс в селекторе

Совмещение тега и класса (например, button.button) повышает специфичность CSS-правил и затрудняет их переопределение.

Рассмотрим такой код:

<button class="button">...</button>

Зададим CSS-правила в селекторе button.button.

И добавим блоку модификатор active:

<button class="button button_active">...</button>

Селектор .button_active не переопределит свойства блока, записанные как button.button, так как специфичность button.button выше чем у .button_active. Чтобы повысить специфичность, селектор модификатора блока также нужно скомбинировать с тегом button.button_active.

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

Возможные исключения

Методология в редких случаях допускает объединение селекторов тега и класса, например, для стилизации комментариев в CMS-системах, которые не позволяют генерировать правильную разметку.

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

<div class="content">
... <!-- текст пользователя -->
</div>

CSS-правила:

.content a {
...
}
.content p {
font-family: Arial, sans-serif;
text-align: center;
}

Не используем селекторы атрибутов

Селекторы атрибутов по информативности уступают селекторам классов. Чтобы доказать это, рассмотрим пример с формой поиска в шапке:

<header>
<form action="/">
<input name="s">
<input type="submit">
</form>
</header>

Воспользуемся селекторами атрибутов, чтобы записать стили для формы:

header input[type=submit],
header input[type=checkbox] {
width: auto;
margin-right: 20px;
}
header input[type=checkbox] {
margin: 0;
}

В таком примере по именам селекторов невозможно однозначно определить, что стили относятся к форме поиска. Нагляднее записать классами. В классах нет ограничений, чтобы писать понятно.

Можно записать, например, так:

.form .search {
...
}

Код стал более однозначным: теперь понятно, что стили относятся к форме, которая отвечает за поиск.

Но вложенность селекторов по-прежнему повышает специфичность CSS-правил и мешает безболезненно переносить верстку из проекта в проект. Чтобы избавиться от вложенности, воспользуемся принципами БЭМ.


Короткие выводы

Классы — единственный селектор, который позволяет изолировать стили каждого компонента в проекте, повысить читаемость кода и не ограничивать повторное использование верстки.

БЭМ. От теории к практике

  • Основы БЭМ
  • Модификаторы и миксы
  • Удобство параллельной разработки и верстка блоками
  • Блоки в файловой структуре
  • Шаблоны в БЭМ
  • Тестирование верстки
  • Сборка проекта
  • С чего начать?

Основы БЭМ

Методология БЭМ — это набор универсальных правил, которые можно применять независимо от используемых технологий, будь то CSS, Sass, HTML, JavaScript или React.

БЭМ помогает решить следующие задачи:

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

В БЭМ-проекте любой интерфейс делится на блоки, которые могут содержать элементы. Блоки — это независимые компоненты страницы. Элементы не существуют вне блока. Каждый элемент может принадлежать только одному блоку.

Именно блокам и элементам посвящены первые две буквы в аббревиатуре БЭМ.

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

Чтобы показать всю силу БЭМ-нейминга, рассмотрим пример с формой. По БЭМ-методологии форма будет представлена блоком form. В HTML имя блока всегда записывается в атрибуте class:

<form class="form" action="/">

Все части формы (блок form), которые не имеют смысла в отрыве от нее, считаются ее элементами. Таким образом поле ввода (search) и кнопка (submit) — это элементы блока form. Принадлежность элемента к блоку также выражается в имени через классы:

<form class="form" action="/">
<input class="form__search" name="s">
<input class="form__submit" type="submit">
</form>

Обратите внимание, что имя блока отделяется от имени элемента специальным разделителем. В классической схеме именования в БЭМ для разделителя используются два подчеркивания. Разделители могут быть любыми. Существуют альтернативные схемы, и каждый разработчик выбирает наиболее удобную для себя. Важно, чтобы разделители давали возможность на программном уровне отличать блоки от элементов и модификаторов.

Из имен селекторов очевидно, что для переноса формы в другой проект, необходимо скопировать все ее составляющие:

.form__search {}
.form__submit {}

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

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

Суть именования компонентов в БЭМ в том, что в имени можно явно указать связь блока и его элементов.

Третья буква в аббревиатуре БЭМ

Официально буква «М» означает «модификатор», но негласно под нее попадает еще одно очень важное понятие в БЭМ — «микс». И модификаторы и миксы изменяют блок и его элементы. Давайте рассмотрим подробнее.

Модификаторы

Модификатор определяет внешний вид, состояние и поведение блока либо элемента.

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

Разберемся, как работают модификаторы.

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

header .form {}
footer .form {}

Действительно, вес селектора header .form выше, чем у form, значит правила будут перекрываться. Но мы уже обсудили, что вложенность селекторов связывает код и мешает его переиспользовать. Значит такое решение не подходит.

В БЭМ можно добавить блоку новые стили с помощью модификатора:

<!-- Добавили модификатор form_type_original -->
<form class="form form_type_original" action="/">
<input class="form__search" name="s">
<input class="form__submit" type="submit">
</form>

Запись <form class="form form_type_original"></form> говорит о том, что блоку form присвоен модификатор type со значением original. В классической схеме имя модификатора отделяется от имени блока или элемента одним подчеркиванием.

Форма может быть уникальна по разным показателям — цвету, размеру, типу, теме оформления. Все эти параметры можно задать с помощью модификатора:

<form class="form form_type_original form_size_m form_theme_forest"></form>

Одна и та же форма может выглядеть по-разному и при этом быть одного размера:

<form class="form form_type_original form_size_m form_theme_forest"></form>
<form class="form form_type_original form_size_m form_theme_sun"></form>

При этом селекторы для каждого модификатора все равно будут иметь одинаковый вес:

.form_type_original {}
.form_size_m {}
.form_theme_forest {}
Важно! Модификатор содержит только дополнительные стили, которые как-то изменяют исходную реализацию блока. Это позволяет один раз написать, как должен выглядеть универсальный блок, и добавить в стили модификатора только те свойства, которые отличают блок от его исходного вида.
.form {
/* стили для универсального блока */
}
.form_type_original {
/* дополнительные стили */
}

Поэтому модификатор всегда должен находиться на одном DOM-узле с блоком или элементом, к которому он относится.

<form class="form form_type_original"></form>

Модификатор позволяет получать очень частные случаи использования универсальных компонентов. При этом код блока или элемента не меняется, на DOM-узле просто создается нужная комбинация из модификаторов.

Миксы

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

Миксом называется одновременное размещение нескольких БЭМ-сущностей (блоков, элементов, модификаторов) на одном DOM-узле.

Миксы, как и модификаторы, используются, чтобы изменять блоки. Разберем примеры, когда нужно применять миксы.

Различия блоков могут быть не только визуальными: блоки могут быть семантически разными. Например, форма поиска, форма регистрации и форма заказа товаров — это все формы, и в верстке они будут выражены блоком form, хотя общих стилей у них практически не будет. Отобразить такие отличия с помощью модификаторов невозможно.

Можно вынести общие стили для таких блоков, но повторно использовать такой код не получится.

.form,
.search,
.register {
...
}

Рассмотрим, как можно создать семантически разные блоки с помощью микса на примере все той же формы:

<form class="form" action="/">
<input class="form__search" name="s">
<input class="form__submit" type="submit">
</form>

В селекторе класса .form описаны все стили, которые могут быть у любой формы (заказов, поиска или регистрации):

.form {}

Теперь предстоит сделать из универсальной формы форму поиска. Для этого в проекте необходимо создать дополнительный класс search, который отвечает только за поиск. Чтобы объединить стили и поведение класса .form и .search, нужно разместить эти классы на одном DOM-узле:

<!-- Микс блоков form и search -->
<form class="form search" action="/">
<input class="form__search" name="s">
<input class="form__submit" type="submit">
</form>

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

Рассмотрим еще один пример, когда меняется семантика компонента. Для примера возьмем навигационное меню в шапке страницы, в котором все пункты должны быть ссылками:

<nav class="menu">
<a class="link" href=""></a>
<a class="link" href=""></a>
<a class="link" href=""></a>
</nav>

Нужная функциональность для ссылок уже реализована в блоке link. Но ссылки в меню должны отличаться визуально от ссылок в тексте. Существует несколько способов изменить ссылки в меню:

1. Создать модификатор для пункта меню, который превратит пункт в ссылку:

<nav class="menu">
<a class="menu__item menu__item_link" href=""></a>
<a class="menu__item menu__item_link" href=""></a>
<a class="menu__item menu__item_link" href=""></a>
</nav>

В таком случае для реализации модификатора придется скопировать поведение и стили блока link. Это приведет к дублированию кода.

2. Воспользоваться миксом универсального блока link и элемента item блока menu:

<nav class="menu">
<a class="link menu__item" href=""></a>
<a class="link menu__item" href=""></a>
<a class="link menu__item" href=""></a>
</nav>

Микс двух БЭМ-сущностей позволит применить базовую функциональность ссылок из блока link и дополнительные CSS-правила из блока menu, и не копировать код.

Внешняя геометрия и позиционирование. Отказываемся от абстрактных HTML-оберток

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

В БЭМ стили, отвечающие за внешнюю геометрию и позиционирование, задаются через родительский блок.

Рассмотрим на примере универсального блока меню, который нужно разместить в шапке. В верстке блок меню должен отступать от родительского блока на 20px.

Существует несколько решений для этой задачи:

1. Написать стили с отступами самому блоку меню:

.menu {
margin-left: 20px;
}

В таком случае блок menu перестанет быть универсальным. Если понадобится разместить меню в подвале страницы, придется править стили, потому что отступы скорее всего будут другими.

2. Создать модификатор для блока меню:

<div>
<ul class="menu menu_type_header">
<li class="menu__item"><a href=""></a></li>
<li class="menu__item"><a href=""></a></li>
<li class="menu__item"><a href=""></a></li>
</ul>
</div>

CSS:

.menu_type_header {
margin-left: 20px;
}
.menu_type_footer {
margin-left: 30px;
}

В таком случае в проекте появятся два типа меню, хотя это не так. Меню остается одно и то же.

3. Определить внешнее позиционирование блока — вложить блок menu в абстрактную обертку (например, блок wrap), где задать все отступы:

<div class="wrap">
<ul class="menu">
<li class="menu__item"><a href=""></a></li>
<li class="menu__item"><a href=""></a></li>
<li class="menu__item"><a href=""></a></li>
</ul>
</div>

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

Отступ от родительского блока — это не свойство вложенного блока быть с таким отступом. Это свойство родительского блока — знать, что вложенный в него блок должен отступать от границы на определенное количество пикселей.

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

Продолжим рассматривать пример:

<div>
<ul class="menu header__menu">
<li class="menu__item"><a href=""></a></li>
<li class="menu__item"><a href=""></a></li>
<li class="menu__item"><a href=""></a></li>
</ul>
</div>

Здесь внешняя геометрия и позиционирование блока menu задана через элемент header__menu. Блок menu не специфицирует никакие отступы и может быть легко использован повторно.

Элемент родительского блока (в нашем случае это header__menu) полностью решает задачу абстрактных блоков-оберток, отвечающих за внешнее позиционирование блока.

Удобство параллельной разработки

В БЭМ любой макет делится на блоки. Благодаря тому, что блоки не зависят друг от друга напрямую, они могут разрабатываться параллельно разными разработчиками. Разработчик создает блок как универсальный компонент, который может быть переиспользован в любом другом проекте.

В качестве примера рассмотрим библиотеку блоков bem-components, которая содержит универсальные блоки, такие как ссылка, кнопка, поле ввода. Из универсальных компонентов легко создавать более сложные блоки. Например, селект или чекбокс.

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

Блоки в файловой структуре

Все БЭМ-проекты имеют схожую файловую структуру. Привычное для разработчиков расположение файлов облегчает навигацию по проекту, упрощает переключение между проектами и перенос блоков из одного проекта в другой.

Реализация каждого блока хранится в отдельной папке проекта. Каждой технологии (CSS, JavaScript, тестам, шаблонам, документации, картинкам) соответствует отдельный файл.

Например, если внешний вид блока input задан с помощью CSS, то код будет сохранен в файле input.css.

project
common.blocks/
input/
input.css # Реализация блока input в технологии CSS
input.js # Реализация блока input в технологии JavaScript

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

project
common.blocks/
input/
input.css # Реализация блока input в технологии CSS
input.js # Реализация блока input в технологии JavaScript
input_theme_sun.css # Реализация модификатора input_theme_sun
input__clear.css # Реализация элемента input__clear в технологии CSS
input__clear.js # Реализация элемента input__clear в технологии JavaScript

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

Файловая структура любого БЭМ-проекта состоит из уровней переопределения. Уровни переопределения позволяют:

Подробнее про уровни переопределения.

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

Шаблонизация в БЭМ

В HTML разметка блока повторяется каждый раз, когда блок встречается на странице. Если разработчик пишет HTML вручную, исправлять ошибку или вносить дополнительные изменения необходимо в каждом экземпляре блока в разметке. Чтобы генерировать HTML-код и применять правки автоматически, в БЭМ используются шаблоны: блоки сами отвечают за то, как они будут представлены в HTML.

Шаблоны позволяют:

  • Сократить время на отладку проекта, так как изменения в шаблоне автоматически применятся ко всем блокам проекта.
  • Изменять разметку блока.
  • Переносить блоки с текущей разметкой в другой проект.

В БЭМ используется шаблонизатор bem-xjst, который содержит два движка:

  • BEMHTML — преобразует BEMJSON-описание страницы в HTML. Шаблоны описываются в файлах с расширением .bemhtml.js.
  • BEMTREE —преобразует данные в BEMJSON. Шаблоны описываются в BEMJSON-формате в файлах с расширением .bemtree.js.
Подробнее о BEMJSON-формате входных данных.

Если шаблоны к блокам не написаны, шаблонизатор по умолчанию установит блокам тег <div>.

Сравните декларацию блоков и выходной результат HTML:

Декларация:

{
block: 'menu',
content: [
{
elem: 'item',
content: {
block: 'link'}
},
{
elem: 'item',
elemMods: { current: true }, // Задали модификатор пункту меню
content: {
block: 'link'
}
}
]
}

HTML:

<div class="menu">
<div class="menu__item">
<div class="link"></div>
</div>
<div class="menu__item menu__item_current">
<div class="link"></div>
</div>
</div>

Чтобы изменить разметку блока menu, необходимо написать шаблоны для блока:

1. Меняем тег блока menu:

block('menu')(
tag()('menu') // Устанавливаем тег menu для блока меню
)

Измененный HTML:

<menu class="menu"> <!-- Заменяем тег div на menu для блока menu -->
<div class="menu__item">
<div class="link"></div>
</div>
<div class="menu__item menu__item_current">
<div class="link"></div>
</div>
</menu>

По аналогии с CSS, шаблон будет применен ко всем блокам menu на странице.

2. Добавляем дополнительный элемент (menu__inner), который выполнит функцию внутренней обертки и будет отвечать за расположение элементов внутри блока menu. Изначально элемент menu__inner не указывался в декларации, поэтому необходимо добавить его на этапе сборки шаблонов.

Шаблоны в БЭМ написаны на JavaScript, поэтому добавить новый элемент в шаблон также можно с помощью JavaScript:

block('menu')(
tag()('menu'),
content()(function() {
return {
elem: 'inner',
content: this.ctx.content
};
})
)

HTML:

<menu class="menu"> <!-- Заменяем тег div на menu для блока menu -->
<div class="menu__inner">
<div class="menu__item">
<div class="link"></div>
</div>
<div class="menu__item menu__item_current">
<div class="link"></div>
</div>
</div>
</menu>

3. Изменяем теги всем элементам inner и item:

block('menu')(
tag()('menu'),
content()(function() {
return {
elem: 'inner',
content: this.ctx.content
}
}),
elem('inner')(
tag()('ul')
),
elem('item')(
tag()('li')
)
)

HTML:

<menu class="menu">
<ul class="menu__inner">
<li class="menu__item">
<div class="link"></div>
</li>
<li class="menu__item menu__item_current">
<div class="link"></div>
</li>
</ul>
</menu>

4. Выставляем тег <a> всем ссылкам на странице:

block('menu')(
tag()('menu'),
content()(function() {
return {
elem: 'inner',
content: this.ctx.content
}
}),
elem('inner')(
tag()('ul')
),
elem('item')(
tag()('li')
)
);
block('link')(
tag()('a')
);

HTML:

<menu class="menu">
<ul class="menu__inner">
<li class="menu__item">
<a class="link"></a>
</li>
<li class="menu__item menu__item_current">
<a class="link"></a>
</li>
</ul>
</menu>

5. Изменяем существующий шаблон. Правила в шаблонах применяются так же, как в CSS: нижнее правило перекрывает верхнее. Добавим новые правила в шаблон, изменим тег ссылкам с <a> на <span>:

block('link')(
tag()('a')
);
block('link')(
tag()('span')
);

HTML:

<menu class="menu">
<ul class="menu__inner">
<li class="menu__item">
<span class="link"></span>
</li>
<li class="menu__item menu__item_current">
<span class="link"></span>
</li>
</ul>
</menu>

Тестирование верстки

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

В БЭМ каждый блок покрывается тестами. Тесты — это такая же технология реализации блока, как JavaScript или CSS. Блоки тестируются на этапе разработки. Проще проверить правильность работы одного блока и потом собрать проект из гарантированно протестированных блоков. После этого останется только убедится, что обвязка для блоков работает правильно.

Сборка проекта

Для удобства работы с кодом в БЭМ-проекте все блоки и технологии разложены по отдельным папкам и файлам. Чтобы объединить исходные файлы в один (например, все CSS-файлы в project.css, все JS-файлы в project.js и т. п.), используется сборка.

Сборка решает следующие задачи:

  • Объединяет исходные файлы, разложенные по файловой структуре проекта.
  • Подключает в проект только необходимые блоки, элементы и модификаторы (БЭМ-сущности).
  • Учитывает порядок подключения.
  • Обрабатывает код исходных файлов в процессе сборки (например, компилирует LESS-код в CSS-код).

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

Так как в БЭМ блоки разрабатываются независимо, находятся в разных файлах файловой системы, они ничего не знают друг о друге. Чтобы одни блоки могли строиться на основании других, необходимо указывать зависимости. За это отвечает отдельная технология в БЭМ — файлы deps.js. По файлам зависимостей инструмент сборки понимает, какие блоки дополнительно подключить в проект.

С чего начать?

Разработчики БЭМ создали шаблонный проект project-stub, в который по умолчанию подключены технологии и библиотеки БЭМ. Он содержит необходимый минимум конфигурационных файлов и директорий, чтобы быстро развернуть проект с нуля.