AzaBEM – лаконичная модификация БЭМ-методологии

Вероятно, все наслышаны о методологии БЭМ, разработанной Яндексом. Кому-то она нравится, кому-то нет. Кто-то считает её совершенной, кто-то — избыточной. Я считаю идею БЭМ отличной, но зачастую она используется не совсем оптимально. Отсюда появляются возгласы о её громоздкости и нелаконичности выходного кода.

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

  • Абсолютное отсутствие лаконичности в названиях классов;
  • Неоправданный отказ от многих возможностей CSS-селекторов;
  • Недостаточное уделение внимания префиксам, которые могли бы оказать заметную помощь в достижении большей читабельности кода.

Из-за этих недостатков БЭМ непрерывно модифицировался мною, и в итоге получилось найти такую схему, которая остаётся лаконичной, лёгкой в поддержке, а также подходит для использования во всех моих проектах (от мала до велика).
В тексте статьи я буду называть её AzaBEM, так как разработали её в AzaGroup.

Вот как выглядит среднестатистический класс при данном подходе:

Structure of CSS-class-names in PBEM methodology

Никаких b-head-stripe_theme_simple-red здесь не будет. Только то, что указано на рисунке. Не больше.

Вот как это может выглядеть в DOM:

Simple HTML-tree that uses PBEM method

Ниже — несколько слов о каждой части.

Block

Это единственная обязательная часть. Блоком является какой-либо достаточно самостоятельный объект:
tagsList — список тегов;
menu — меню;
btn — кнопка.

Element

Является частью блока:
tagsList-tag — тег в списке тегов;
menu-item — ссылка в меню;
btn-icon — иконка перед текстом в кнопке.

Modifier

Модификатор, добавляемый любому объекту:
tagsList-tag_active — активный тег;
menu_bottom — меню внизу страницы;
btn-icon_folder — иконка папки.

Одному классу можно приписывать лишь один модификатор. Если нужно больше, пишем так:
btn-icon_folder btn-icon_big — большая иконка папки.

В отличие от классического БЭМ, где названия модификаторов состоят из двух частей (_size_big), в AzaBEM названия модификаторов обычно выглядят проще: _big.

В самом деле, сколько модифицированных состояний может быть, например, у пункта меню? Штуки три: _first, _last, _current. И всё. Так зачем же усложнять модификаторы и писать: _position_first, _position_last, _state_current?

Prefix

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

l- layout. Используется для структурных объектов страницы. Это могут быть: шапка сайта, главное меню, футер, а также любые другие части, которые «вшиты» в шаблон страницы и имеют строго определённое место. Например, логотип рядом с главным меню, который присутствует на всех (или многих) страницах в одном и том же месте, будет иметь класс l-logo.

e- element. Любой выделенный элемент, повторяющийся на нескольких страницах. Это может быть кнопка, список тегов, социальные иконки, JS-плагины календарика, модальных окон, туллтипов и пр. Все они повторяются на нескольких страницах, на разных местах, в разных комбинациях.

Вероятно, в этом неймспейсе будет находиться больше всего классов, поэтому давать имена блокам с префиксом e- нужно предусмотрительно, соблюдая некоторые заранее выработанные правила. У меня правило лишь одно: названия блоков JS-плагинов начинаются с большой буквы — e-Calendar, e-Tootlip, e-Modal.
Остальные названия — с маленькой: e-btn, e-dropdown, e-table.

g- global. Такие префиксы даются «модифицирующим классам». То есть классам, которые навешиваются на любой объект на странице и модифицируют его. К таким классам можно отнести: g-clrfix, g-bold, g-inlineBlock и пр. Все они не являются самостоятельными элементами, а лишь изменяют свойства других объектов.

p- page. Данный префикс будет означать, что данный объект встречается только на одной странице (или группе однородных страниц одного раздела сайта) и не повторяется по всему сайту. Например, блок информации о пользователе и блок друзей встречаются только на странице профиля. Желательно такие классы хранить в отдельном CSS-файле. Об этом ниже.

Кроме того, можно создавать классы без префикса. Такие классы указываются не для объектов на странице, а для само́й страницы. Обычно они устанавливаются для body или html. Так работает, например, Modernizr. Итак, классы без префикса могут быть использованы для:

  • Определения типа браузера: webkit, ff, ie;
  • Определения возможностей браузера: boxshadow, no-boxshadow;
  • Указания режима отображения страницы: flexLayout, scrolling.

Отдельно нужно отметить ещё один префикс.

j- javascript. Это единственный префикс, классы которого ни в коем случае нельзя стилизовать. Классы с данным префиксом используются только для доступа из JS: $loginForm.find(’.j-sendBtn’).

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

Тем не менее, иногда всё же приходится использовать в JS не j-классы.

Во-первых, когда JS генерирует HTML.
Вышеупомянутые плагины календарика и модалок имеют классы с префиксом e-, и всё это генерируется на клиенте. Хотя если вы используете шаблоны, которые передаются с бэкенда, то тут проблем нет никаких. Главное — не забудьте проставить j-классы, и JS будет работать только с ними.

Во-вторых, когда необходимо сменить состояние объекта.
К примеру, отобразить скрытый прелоадер, добавив ему класс e-loader_visible. Это допустимо, но крайне нежелательно. В таких случаях я рекомендую использовать атрибуты: просто установите значение атрибута visible, например, в true. В CSS необходимо будет указать соответствующий селектор: e-loader[visible=true] { ... }.

CSS-файлы на странице

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

При использовании подхода AzaBEM на странице достаточно подключать всего 2 файла стилей:

  1. Файл с общими для всего сайта классами. Там будут: классы разметки, элементы интерфейса, виджеты и пр. Все эти классы имеют свои префиксы;
  2. Файл с локальными классами для данной страницы. Здесь собраны все стили, которые нужны лишь на данной странице (или в группе однородных страниц). Такие классы, как упоминалось выше, имеют префикс p-.

Иногда для крупных проектов будет необходимо разделить первый файл на несколько. Например, в соответствии с количеством разделов сайта (раздел аудиозаписей, личных сообщений и пр.). Тогда на странице будут загружаться 3 файла:

  1. Файл с базовыми стилями для всего сайта;
  2. Файл с базовыми стилями для данного раздела;
  3. Файл с локальными стилями для данной страницы.

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

  • Главная страница сайта: common.css + main-page.css.
  • Форум: common.css + forum.css.
  • Поиск по форуму имеет много форм, которые используются только на странице поиска, поэтому мы отделили эти стили от основных стилей форума:
    common.css + forum.css + forum-search.css.
В файлах common.css и forum.css будет довольно много стилей с префиксом e-. Если вы опасаетесь, что названия этих стилей могут пересечься, введите дополнительный префикс, напрмер, s- (section), и используйте его в файле forum.css вместо стандартного e-.

Вложенные элементы и блоки

Иногда классам предпочитают давать такие названия, которые позволят проследить всё HTML-дерево, в котором находится элемент.
К примеру: .menu{}, .menu-list{}, .menu-list-item{}, .menu-list-item-link{}.

Я против такого разврата, и предпочитаю указывать лишь имя конечного элемента:
.menu{}, .menu-list{}, .menu-item{}, .menu-link{}.

Более того, в данном примере я считаю избыточностью указание класса menu-link для каждой ссылки, и в CSS вполне допускаю такие записи: .tagsList-item > a или .tagsList-item_active > a. При наличии множества пунктов меню, это может заметно сократить вес страницы.

В итоге DOM может выглядеть следующим образом:

PBEM example of DOM-structure that contains nested blocks

В данной структуре в блок l-menu вложен блок e-search. Последнему также добавлен класс l-menu-search для того, что бы указать дополнительные стили данной форме поиска. Если бы эта форма в дополнительных стилях не нуждалась, то класс l-menu-search можно было бы не прописывать.

Вообще, добавление форме поиска «стороннего» класса l-menu-search — это не очень хорошая практика. Гораздо лучше было бы воспользоваться модификатором.

К примеру, если форма поиска, находящаяся в меню, должна отображаться слега уменьшенной, то можно просто сделать модификатор (e-search_small или даже e-search_mod1) и пользоваться им.

Если же поиск в меню заметно отличается от обычного, то разумно было бы и вовсе сделать отдельный класс e-searchSmall. Это позволит работать с двумя данными блоками совершенно независимо.

Третьему пункту меню указан класс g-bold, который делает этот пункт жирным.

В качестве примера форме также добавлены классы с префиксом j-. Как видите, жёстких требований по именованию данных j-классов у меня нет. Обычно я просто указываю любое понятное имя.

Также, ради лучшего восприятия кода, не забывайте соблюдать порядок указания классов. Так, модифицирующий класс l-menu-item_active должен идти после базового класса l-menu-item, а e-search-btn — после e-btn.

Резюме

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

  • Отличная читабельность выходного кода;
  • Отсутствие необходимости подбирать сложные уникальные названия блоков за счёт удобного использования префиксов;
  • Несвязанность представления с логикой: мы можем менять вёрстку, не боясь, что после этого «отвалится» весь JS;
  • Минимальный объём кода при максимальной гибкости;
  • Загрузка не более трёх CSS-файлов на одной странице.