Введение

БЭМ (Блок, Элемент, Модификатор) - компонентный подход к веб-разработке. В его основе лежит принцип разделения интерфейса на независимые блоки. Он позволяет легко и быстро разрабатывать интерфейсы любой сложности и повторно использовать существующий код, избегая «Copy-Paste».


Блок

Функционально независимый компонент страницы, который может быть повторно использован. В HTML блоки представлены атрибутом class.

Особенности:

  • Название блока характеризует смысл («что это?» - «меню»: menu, «кнопка»: button), а не состояние («какой, как выглядит?» - «красный»: red, «большой»: big).

Пример:

<!-- Верно. Семантически осмысленный блок `error` -->
<div class="error"></div>

<!-- Неверно. Описывается внешний вид -->
<div class="red-text"></div>
  • Блок не должен влиять на свое окружение, т. е. блоку не следует задавать внешнюю геометрию (в виде отступов, границ, влияющих на размеры) и позиционирование.
  • В CSS по БЭМ также не рекомендуется использовать селекторы по тегам или id.

Таким образом обеспечивается независимость, при которой возможно повторное использование или перенос блоков с места на место.

- Принципы работы с блоками

  • Блоки можно вкладывать друг в друга.
  • Допустима любая вложенность блоков.
<!-- Блок `header` -->
<header class="header">
    <!-- Вложенный блок `logo` -->
    <div class="logo"></div>

    <!-- Вложенный блок `search-form` -->
    <form class="search-form"></form>
</header>

Элемент

Составная часть блока, которая не может использоваться в отрыве от него.

Особенности:

  • Название элемента характеризует смысл («что это?» - «пункт»: item, «текст»: text), а не состояние («какой, как выглядит?» - «красный»: red, «большой»: big).
  • Структура полного имени элемента соответствует схеме: имя-блока__имя-элемента. Имя элемента отделяется от имени блока двумя подчеркиваниями (__).

Пример:

<!-- Блок `search-form` -->
<form class="search-form">
    <!-- Элемент `input` блока `search-form` -->
    <input class="search-form__input">

    <!-- Элемент `button` блока `search-form` -->
    <button class="search-form__button">Найти</button>
</form>

- Принципы работы с элементами

Вложенность

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

Пример:

<!--
    Верно. Структура полного имени элементов соответствует схеме:
    `имя-блока__имя-элемента`
-->
<form class="search-form">
    <div class="search-form__content">
        <input class="search-form__input">
        <button class="search-form__button">Найти</button>
    </div>
</form>

<!--
    Неверно. Структура полного имени элементов не соответствует схеме:
    `имя-блока__имя-элемента`
-->
<form class="search-form">
    <div class="search-form__content">
        <!--
            Рекомендуется:
            `search-form__input` или `search-form__content-input`
        -->
        <input class="search-form__content__input">

        <!--
            Рекомендуется:
            `search-form__button` или `search-form__content-button`
        -->
        <button class="search-form__content__button">Найти</button>
    </div>
</form>

Имя блока задает пространство имен, которое гарантирует зависимость элементов от блока (block__elem).

Принадлежность

Элемент - всегда часть блока и не должен использоваться отдельно от него.

Пример:

<!-- Верно. Элементы лежат внутри блока `search-form` -->
<!-- Блок `search-form` -->
<form class="search-form">
    <!-- Элемент `input` блока `search-form` -->
    <input class="search-form__input">

    <!-- Элемент `button` блока `search-form` -->
    <button class="search-form__button">Найти</button>
</form>

<!-- Неверно. Элементы лежат вне контекста блока `search-form` -->
<!-- Блок `search-form` -->
<form class="search-form">
</form>

<!-- Элемент `input` блока `search-form` -->
<input class="search-form__input">

<!-- Элемент `button` блока `search-form` -->
<button class="search-form__button">Найти</button>

Необязательность

Элемент - необязательный компонент блока. Не у всех блоков должны быть элементы.

Пример:

<!-- Блок `search-form` -->
<div class="search-form">
    <!-- Блок `input` -->
    <input class="input">

    <!-- Блок `button` -->
    <button class="button">Найти</button>
</div>

Когда создавать блок, когда - элемент?

- Создавайте блок

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

- Создавайте элемент

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

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


Модификатор

Cущность, определяющая внешний вид, состояние или поведение блока либо элемента.

Особенности:

  • Название модификатора характеризует внешний вид («какой размер?», «какая тема?» и т. п. - «размер»: size_s, «тема»: theme_islands), состояние («чем отличается от прочих?» - «отключен»: disabled, «фокусированный»: focused) и поведение («как ведет себя?», «как взаимодействует с пользователем?» - «направление»: directions_left-top).
  • Имя модификатора отделяется от имени блока или элемента одним подчеркиванием (_).

- Типы модификаторов

Булевый

  • Используют, когда важно только наличие или отсутствие модификатора, а его значение несущественно. Например, «отключен»: disabled. Считается, что при наличии булевого модификатора у сущности его значение равно true.
  • Структура полного имени модификатора соответствует схеме:
    имя-блока_имя-модификатора;
    имя-блока__имя-элемента_имя-модификатора.

Пример:

<!-- Блок `search-form` имеет булевый модификатор `focused` -->
<form class="search-form search-form_focused">
    <input class="search-form__input">

    <!-- Элемент `button` имеет булевый модификатор `disabled` -->
    <button class="search-form__button search-form__button_disabled">Найти</button>
</form>

Ключ-значение

  • Используют, когда важно значение модификатора. Например, «меню с темой оформления islands»: menu_theme_islands.
  • Структура полного имени модификатора соответствует схеме:
    имя-блока_имя-модификатора_значение-модификатора;
    имя-блока__имя-элемента_имя-модификатора_значение-модификатора.

Пример:

<!-- Блок `search-form` имеет модификатор `theme` со значением `islands` -->
<form class="search-form search-form_theme_islands">
    <input class="search-form__input">

    <!-- Элемент `button` имеет модификатор `size` со значением `m` -->
    <button class="search-form__button search-form__button_size_m">Найти</button>
</form>

<!--
    Невозможно одновременно использовать два одинаковых модификатора
    с разными значениями
-->
<form class="search-form
             search-form_theme_islands
             search-form_theme_lite">

    <input class="search-form__input">

    <button class="search-form__button
                   search-form__button_size_s
                   search-form__button_size_m">
        Найти
    </button>
</form>

- Принципы работы с модификаторами

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

Пример:

<!-- Верно. Блок `search-form` имеет модификатор `theme` со значением `islands`-->
<form class="search-form search-form_theme_islands">
    <input class="search-form__input">

    <button class="search-form__button">Найти</button>
</form>

<!-- Неверно. Отсутствует модифицируемый класс `search-form` -->
<form class="search-form_theme_islands">
    <input class="search-form__input">

    <button class="search-form__button">Найти</button>
</form>

БЭМ-сущность

БЭМ-сущностями называются блоки, элементы и модификаторы.

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


Микс

Способ использования разных БЭМ-сущностей на одном DOM-узле.

Миксы позволяют:

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

Рассмотрим пример микса блока и элемента другого блока.

Допустим, в проекте ссылки реализованы блоком link. Необходимо сделать ссылками пункты меню. Существует несколько способов:

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

БЭМ-дерево

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

Рассмотрим пример DOM-дерева:

<header class="header">
    <img class="logo">
    <form class="search-form">
        <input class="input">
        <button class="button"></button>
    </form>
    <ul class="lang-switcher">
        <li class="lang-switcher__item">
            <a class="lang-switcher__link" href="url">en</a>
        </li>
        <li class="lang-switcher__item">
            <a class="lang-switcher__link" href="url">ru</a>
        </li>
    </ul>
</header>

Ему соответствует такое БЭМ-дерево:

header
    logo
    search-form
        input
        button
    lang-switcher
        lang-switcher__item
            lang-switcher__link
        lang-switcher__item
            lang-switcher__link

Соглашение по именованию

Имя БЭМ-сущности уникально. Во всех технологиях (CSS, JavaScript, HTML) одна и та же БЭМ-сущность всегда называется одинаково. Основная идея соглашения по именованию - вложить смысл в имена и сделать их максимально информативными для разработчика.

Можно сравнить одно и тоже имя CSS-селектора, написанное разными способами:

  • menuitemvisible
  • menu-item-visible
  • menuItemVisible

Чтобы понять смысл первого имени, нужно вчитаться в каждое слово. В последних двух примерах имя явно разделяется на логические части. Но ни одно из имен пока не помогает точно определить, что menu - это блок, item - элемент, а visible - модификатор. Чтобы имена сущностей были однозначными и понятными, в БЭМ были разработаны правила формирования имен БЭМ-сущностей.

- Правила формирования имен

block-name__elem-name_mod-name_mod-val
  • Имена записываются латиницей в нижнем регистре.
  • Для разделения слов в именах используется дефис (-).
  • Имя блока задает пространство имен для его элементов и модификаторов.
  • Имя элемента отделяется от имени блока двумя подчеркиваниями (__).
  • Имя модификатора отделяется от имени блока или элемента одним подчеркиванием (_).
  • Значение модификатора отделяется от имени модификатора одним подчеркиванием (_).
  • Значение булевых модификаторов в имени не указывается.

Важно! В методологии БЭМ не существует элементов элементов. Правила именования запрещают создавать элементы элементов, но в DOM-дереве элементы можно вкладывать друг в друга.

Примеры:

- Имя блока

menu

HTML:
<div class="menu">...</div>

CSS:
.menu { color: red; }

- Имя элемента

menu__item

HTML:
<div class="menu">
  ...
  <span class="menu__item"></span>
</div>

CSS:
.menu__item { color: red; }

- Имя модификатора блока

menu_hidden

menu_theme_islands

HTML:
<div class="menu menu_hidden">...</div>
<div class="menu menu_theme_islands">...</div>

CSS:
.menu_hidden { display: none; }
.menu_theme_islands { color: green; }

- Имя модификатора элемента

menu__item_visible

menu__item_type_radio

HTML:
<div class="menu">
  ...
  <span class="menu__item menu__item_visible menu__item_type_radio">...</span>
</div>

CSS:
.menu__item_visible {}
.menu__item_type_radio { color: blue; }

Альтернативные схемы именования

- Стиль Two Dashes

block-name__elem-name--mod-name--mod-val

  • Имена записываются латиницей в нижнем регистре.
  • Для разделения слов в именах БЭМ-сущностей используется дефис (-).
  • Имя элемента отделяется от имени блока двумя подчеркиваниями (__).
  • Булевые модификаторы отделяются от имения блока или элемента двумя дефисами (--).
  • Значение модификатора отделяется от его имени двумя дефисами (--).

- Стиль CamelCase

blockName__elemName_modName_modVal

  • Имена записываются латиницей.
  • Каждое слово внутри имени пишется с заглавной буквы.
  • Разделители элементов и модификаторов совпадают с классической схемой.

- Стиль React

BlockName-ElemName_modName_modVal

  • Имена записываются латиницей.
  • Имена блоков и элементов пишутся с заглавной буквы. Имена модификаторов - со строчной.
  • Каждое слово внутри имени пишется с заглавной буквы.
  • Имя элемента отделяется от имени блока одним дефисом (-).
  • Разделители имени и значения модификаторов совпадают с классической схемой.