частичный перевод статьи CSS Tricks
БЭМ (Блок Элемент Модификатор) — популярная методика наименований CSS-классов, облегчающая поддержку CSS. Эта статья предполагает, что вы уже знакомы с методологией. Если нет, можете разобраться с основами на getbem.com.
Стандартный синтаксис БЭМ:
block-name__element-name--modifier-name
Я сам — большой фанат методологии, стоящей за принципами БЭМ.
Стили проще поддерживать в виде отдельных компонентов, а не полотна высокоспецифичных селекторов. Однако этот синтаксис несовершенен и может создать проблемы в продакшене и усложнить жизнь разработчикам. Я предпочитаю использовать немного видоизменённую версию синтаксиса. Я зову её — ABEM (Atomic Block Element Modifier):
[a/m/o]-blockName__elementName -modifierName
Префикс Atomic
Префиксы Atomic Design — a/m/o. Не путайте с Atomic CSS — это совершенно другое явление. Атомарный дизайн — это методология организации компонентов для их максимального переиспользования. Атомы — простейшие компоненты, состоящие чаще всего из одного элемента (напр. кнопка). Молекулы — малые группы элементов и/или компонентов (напр. одиночное поле формы с label и input). Организмы — большие и сложные компоненты, собранные из множества молекул и атомов (напр. целая форма регистрации).
Сложность использования атомарного дизайна с классическим БЭМ в отсутствии идентификатора, указывающего на тип компонента. Это может осложнить поиск кода этого компонента, так как вам придётся искать его в трёх отдельных директориях. Добавление атомарного префикса в начало позволяет сразу понять, в какой папке лежит компонент.
camelCase
Облегчает группировку
Классический БЭМ разделяет слова в блоке одиночным дефисом. Обратите внимание: атомарный префикс в примере ниже также отделён от остального имени класса дефисом. А теперь сравните, что происходит при добавлении атомарного префикса к классическому БЭМ и camelCase:
/* classic + atomic prefix */
.o-subscribe-form__field-item {} /* camelCase + atomic prefix */
.o-subscribeForm__fieldItem {}
На первый взгляд, имя компонента в классической версии выглядит как “o subscribe form”. Значимость “o” полностью утрачена. Применение “o-” к camelCase версии делает очевидным выделение префикса как отдельного элемента имени.
Конечно, можно применить атомарный префикс к классическому БЭМ в виде заглавной “О”:
/* classic + capitalized atomic prefix */
.O-subscribe-form__field-item {}
Это решит проблему с потерей “о” на фоне всего имени класса, однако не устранит глубинный конфликт с классическим синтаксисом БЭМ. Разделение слов дефисами лишает вас возможности использовать дефис как механизм группировки. Применение camelCase возвращает дефису функцию дополнительной группировки, даже если она выражается просто в добавлении номера в конце имени класса.
Группы считываются быстрее
У camelCase есть ещё одно преимущество: имена классов считываются быстрее. Каждый пробел в camelCase представляет собой какую-либо группировку. В классическом БЭМ каждый пробел может быть или группировкой, или пробелом между двумя словами в конкретной группе.
Взгляните на силуэт классических БЭМ-классов (с атомарным префиксом) и угадайте, где префикс, блок, элемент или модификатор:
А теперь взгляните на это. Это тот же самый класс, что и в примере выше, но в этот раз написанный с помощью camelCase вместо дефиса для отделения отдельных слов.
Намного проще, правда? Эти силуэты и воспринимает ваш мозг, когда вы просматриваете код. Лишние дефисы в классах запутывают группировку. Когда вы просматриваете код, ваш мозг пытается отделить пробелы между словами от пробелов между группами. Эта неясность создаёт лишнюю нагрузку во время работы.
классический БЭМ + атомарный префикс
camelCase БЭМ + атомарный префикс
Используйте мультиклассовые селекторы (с умом)
Одно из золотых правил БЭМ гласит: один селектор = один класс. Это должно поддерживать низкую специфичность селекторов и их простую поддержку. С одной стороны, низкая специфичность действительно лучше, чем если она пойдёт вразнос. С другой стороны, жёсткое правило “по одному классу на селектор” приносит пользу проектам. Несколько мультиклассовых селекторов в ваших стилях могут повысить поддерживаемость, а не ухудшить её.
Но это же ведёт к повышению специфичности! Это же абсолютное зло!
Специфичность != плохо.
Бесконтрольная специфичность, пошедшая вразнос = плохо.
Несколько классов с высокой специфичностью не обязательно значат, что ваш код тяжелее поддерживать. Применяемые должным образом правила с высокой специфичностью могут сделать CSS более поддерживаемым. Главное — добавлять специфичность намеренно, а не просто потому, что списочный элемент оказался внутри списка.
Кроме того, мы же хотим, чтобы наши модификаторы были сильнее, чем дефолтные стили? Какой смысл сохранять модификаторам ту же специфичность, что и обычным стилям? В каких условиях вам может понадобится переписать дефолтными стилями специально определённые модифкаторы?
Отделение модификаторов очищает HTML
Это крупнейшее изменение, которое предлагает АБЭМ к синтаксису. Вместо того, чтобы соединять модификатор с классом элемента, вы применяете его как отдельный класс.
Начиная изучать БЭМ, многие жалуются на уродливость этого синтаксиса. Всё особенно плохо, когда речь заходит о модификаторах. Взгляните на этот кошмар. У него только три модификатора, а он уже выглядит как товарный поезд.
<button class="block-name__element-name block-name__element-name--small block-name__element-name--green block-name__element-name--active">
Submit
</button>
Посмотрите на все эти повторы! Вы с трудом разберёте, что этот код вообще делает. А теперь взгляните на пример такого же решения в АБЭМ:
<button class="a-blockName__elementName -small -green -active">
Submit
</button>
Намного чище, правда? Гораздо легче понять, для чего нужны модификаторы, когда перед глазами не маячат непрерывные повторы.
В DevTools вы всё ещё будете видеть полную картину, так что связь класса с модификатором сохраняется.
.a-blockName__elementName.-green {
background: green;
color: white;
}
От БЭМ почти не отличается:
.block-name__element-name--green {
background: green;
color: white;
}
Состояние контролируется легче
Большое преимущество АБЭМ перед классическим БЭМ в лёгкости изменения состояния компонента. Давайте приведём в пример классический аккордеон. Когда секция аккордеона открыта, скажем, мы хотим применить такие изменения к стилям:
- Поменять фоновый цвет заголовка.
- Показать контент.
- Перевернуть стрелку.
В этом примере будем придерживаться классического Б__Э--М синтаксиса и правила “один класс на селектор”. Вот что мы получим в итоге:
SCSS выглядит довольно чистым, но смотрите сколько дополнительных классов нужно задать ради всего одного изменения!
HTML при закрытой секции с использованием БЭМ
<div class="revealer accordion__section">
<div class="revealer__trigger">
<h2 class="revealer__heading">Three</h2>
<div class="revealer__icon"></div>
</div>
<div class="revealer__content">
Lorem ipsum dolor sit amet...
</div>
</div>
HTML при открытой секции с использованием БЭМ
<div class="revealer accordion__section">
<div class="revealer__trigger revealer__trigger--open">
<h2 class="revealer__heading">One</h2>
<div class="revealer__icon revealer__icon--open"></div>
</div>
<div class="revealer__content revealer__content--open">
Lorem ipsum dolor sit amet...
</div>
</div>
Теперь давайте взглянем на ту же запись методом А-Б__Э--М:
Один класс теперь контролирует всю стилизацию состояний для всего компонента вместо применения отдельных классов к каждому элементу.