BEMIT: следующий шаг БЭМ-нотации для CSS

Те, кто следит за мной или моей работой, и неважно насколько долго он это делает, знает, что я горячо рекомендую нотацию БЭМ для CSS. То, о чем я хотел бы рассказать в этой статье, является не альтернативой или вариантом синтаксиса БЭМ, а его расширением: это небольшие дополнения, который поднимают БЭМ на новый уровень. Я назвал это расширение синтаксиса BEMIT, т.к. оно берет идеи и некоторые паттерны из (так и не опубликованной) архитектуры обратного треугольника. BEM + ITCSS = BEMIT.

Напомню, БЭМ разделяет все классы на 3 группы:

Блок, Элемент, Модификатор: БЭМ. Абсолютно любой класс в проекте вписывается в одну из этих категорий, поэтому БЭМ так прекрасен своей понятностью и простотой.

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

<div class="media  user  premium">
  <img src="" alt="" class="img  photo  avatar" />
  <p class="body  bio">...</p>
</div>

Наверное вы бы начали с user, но, чтобы удалить остальное, вам бы пришлось догадываться, тратить время на детальное изучение проекта или пытаться разобраться как-то ещё. Однако если мы перепишем это используя БЭМ:

<div class="media  user  user--premium">
  <img src="" alt="" class="media__img  user__photo  avatar" />
  <p class="media__body  user__bio">...</p>
</div>

Так мы сразу можем увидеть что user, user--premium, user__photo и user__bio взаимосвязаны. Также мы можем увидеть, что media, media__img и media__body связанны, а avatar — это просто одинокий Блок без своих собственных Элементов или Модификаторов.

Здорово, что мы узнаем столько деталей всего лишь из имён классов! Это позволяет нам лучше понимать, какую роль играет тот или иной элемент и делать значительно более безопасные и взвешенные решения по поводу того, как их использовать, изменять и удалять.

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

В связи с этим я решил расширить БЭМ до BEMIT. BEMIT не добавляет никаких новых типов классов, у нас по прежнему остаются только Блоки, Элементы или Модификаторы, но он добавляет информацию об использовании и состоянии.

Пространства имён

Чтобы мне не повторяться, вам, возможно, лучше прочесть статью, которую я опубликовал ранее в этом году — More Transparent UI Code with Namespaces, в которой я представил идею добавления префиксов определенного вида для каждого класса, который был пояснял разработчикам что именно делает этот класс. Такое именование в стиле венгерской нотации позволит нам установить какую задачу должен выполнять каждый класс, как и где мы можем использовать его повторно (если можем), можем ли мы его модифицировать — и многое другое. Статья довольно большая, но она позволит вам значительно лучше понять этот подход.

Наиболее распространенные пространства имен это: c- — для компонентов, o- — для объектов, u- — для утилит и is-/has- для состояний (читайте подробнее в статье, упомянутой выше).

С учётом этого, HTML будет выглядеть так:

<div class="o-media  c-user  c-user--premium">
  <img src="" alt="" class="o-media__img  c-user__photo  c-avatar" />
  <p class="o-media__body  c-user__bio">...</p>
</div>

Этот код показывает, что в нём есть пригодная к повторному использованию абстракция (это OOCSSприм. переводчика) в Медиа-объекте (o-media*) и два компонента, которые зависят от контекста применения (c-user* и c-avatar). Эти классы по-прежнему являются Блоками, Элементами или Модификаторами, мы просто добавили им дополнительный уровень смысла.

Эти пространства имён связанны со слоями, которые определены в архитектуре обратного треугольника, у каждого класса теперь есть своё место в проекте (и файловой системе).

Адаптивные суффиксы

Следующее, что BEMIT добавляет к традиционной БЭМ-нотации, это адаптивные суффиксы. Они имеют формат @<breakpoint> и описывают связь этого класса с медиасостоянием.

<div class="o-media@md  c-user  c-user--premium">
  <img src="" alt="" class="o-media__img@md  c-user__photo  c-avatar" />
  <p class="o-media__body@md  c-user__bio">...</p>
</div>

Например, тут есть o-media@md, что означает, что это медиаобъект для набора правил медиа состояния md. Вот ещё возможные варианты:

Символ @ легко читается и имеет логическое значение для обозначения условных состояний. Это позволяет разработчикам, просто взглянув на него, понять, какие потенциальные перестановки или изменения могут быть у данного элемента интерфейса.

Примечание: в CSS-файле символ @ необходимо экранировать:

@media print {
  .u-hidden\@print {
    display: none;
  }
}

Проверка жизнеспособности кода

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

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

/**
 * Выделить все элементы с атрибутом class.
 */
[class] {
  outline: 5px solid lightgrey;
}

/**
 * Выделить все БЭМ-элементы.
 */
[class*="__"] {
  outline: 5px solid grey;
}

/**
 * Выделить все элементы с БЭМ-модификаторами.
 */
[class*="--"] {
  outline: 5px solid darkgrey;
}

/**
 * Выделить все элементы объектов
 */
[class^="o-"],
[class*=" o-"] {
  outline: 5px solid orange;
}

/**
 * Выделить все элементы компонентов
 */
[class^="c-"],
[class*=" c-"] {
  outline: 5px solid cyan;
}

/**
 * Выделить все элементы с адаптивными суффиксами.
 */
[class*="@"] {
  outline: 5px solid rosybrown;
}

/**
 * Выделить все элементы с классами-хаками.
 */
[class^="_"] {
  outline: 5px solid red;
}

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

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

.s-healthcheck {

  ...

  /**
   * Выделить все элементы с адаптивными суффиксами.
   */
  [class*="@"] {
    outline: 5px solid rosybrown;
  }

  ...

}

…и добавлять его элементу html, когда будет нужно:

<html class="s-healthcheck">

Заключение

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

.c-page-head {}

@media screen and (min-width: 15em) {
  .u-text-center\@sm {}
}

.o-layout__item {}

@media print {
  .u-color-black\@print {}
}