АБЭМ. Удобная адаптация БЭМ.

Странник
5 min readFeb 21, 2018

частичный перевод статьи 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 Designa/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>

Теперь давайте взглянем на ту же запись методом А-Б__Э--М:

Один класс теперь контролирует всю стилизацию состояний для всего компонента вместо применения отдельных классов к каждому элементу.

--

--