БЭМ
Last updated
Last updated
БЭМ (Блок, Элемент, Модификатор) — методология именования структурных элементов интерфейса, придуманная Яндексом. БЭМ позволяет команде лучше взаимодействовать, это понятный язык, на котором говорят верстальщики. Рутинные задачи (придумать названия классов, организовать стили) решаются сами собой. Разработчику, который пришел на любой проект, проще понять код. Не нужно подстраиваться под каждый проект, нужно только один раз разобраться с БЭМ-нотацией.
Далее перечислены некоторые особенности работы с БЭМ, сложившиеся в Оджетто. Примеры кода приведены в LESS.
HTML и CSS изначально придуманы для оформления текстов. Они не предназначены для реализации сложных интерфейсов.
Для оформления текстов каскад вполне хорош, но нам надо делать сложные дизайны, динамические элементы, повторяющиеся в разных местах блоки и для этого нужно что-то более изящное, чем каскад.
БЭМ помогает создать плоскую структуру стилей и сделать блоки уникальными без каскада. Каскад в БЭМе становится помощником, а не догмой и не связывает руки.
Часто каскад используется для элементов блока с модификатором:
Каскад для блоков так же может быть использован, если нужно специально сделать блок зависимым от контекста:
Еще случай каскада для блоков. Например, есть блок .pagination
. Он встречается на странице Категории, Товара, FAQ и вообще в любом разделе, где есть разбивка по страницам. Вид у блока одинаковый, но есть нюансы в отступах, положению и пр. Мы можем сделать так:
Используем только классы, нельзя именовать блоки через id
: .menu
, .page-footer
и т. д.
Если класс состоит из нескольких слов, то их нужно разделять -
: .page-footer
.
В БЭМ для элементов могут использоваться префиксы: l-
, g-
, b-
и пр. Нам они особо не нужны. Незачем вводить сущности там, где можно без них обойтись. Используем только префикс .js-
для навешивания обработчиков. Для таких классов нельзя писать стили, они используются только в JS.
Именовать js-
блоки нужно так же, как обычные классы — через -
, не камелкейсом: .js-btn-checkout
. Камелкейс только для айдишников.
Имена блоков должны отображать сущность блока, его смысл, а не его внешний вид:
Однако, иногда попадаются дизайны, где сложно придумать имена классов исходя из логики поведения элементов. В таком случае можно задать модификаторы и по внешнему виду: .btn_small
, .btn_red
. Но только в том случае, когда реально не подойдет, например, .btn_secondary
или .btn_primary
.
Не нужно бояться создавать новые блоки. Если возникает желание сделать элемент у элемента (что не правильно), то, возможно, стоит ввести новый блок.
Префиксы добавляют сложности, повышают порог вхождения, но дают больше возможностей по организации структуры.
Мы не используем префиксы, кроме .js-
. Ништяки от их использования не перекрывают их сложности в наших реалиях. Без них мы ничего не теряем, без них новеньким проще врубиться в БЭМ.
Пример:
.l-
— префикс для лайаутных блоков. Тех, которые отвечают чисто за позиционирование, ширину, без визуального представления.
.b-
— основной префикс.
.h-
— внешняя обертка блока.
.js-
— префикс для блоков, на которые навешивается JS-обработчики.
Мадженто генерирует статические страницы. Верстка для них вставляется в темплейты руками, если есть заверстанные макеты.
Стили и скрипты для компонента (блок, который будет встречаться на разных страницах) нужно класть в отдельную папку:
Далее этот блок можно импортить на разных страницах. Например, блок related-products
может быть показан на странице категории и на корзине:
Верстку вставляем в .phtml
-файлы вручную, стили и скрипты блока импортим так:
Некоторые блоки (попапы, например) можно подключать через require.ensure()
, если используется Вебпак или асинхронный require
, если используется AMD.
Блок может быть элементом другого блока. В БЭМ это называется миксование.
Например, пост в блоге:
Зачем так делать? Для .post
мы задаем стили такие, что позволяют его переносить с места на место. Для .blog__item
мы задаем стили такие, которые форматируют статью так, как это нужно внутри блока .blog
. Таким образом .post
независим и в то же время как-то изменяется в контексте положения внутри .blog
.
Возможный косяк: стили могут конфликтовать. Что-то внутри .post
может перетереть стили для .blog__item
. Выход: можно делать так (это решение более стабильное):
Можно так же добавить модификатор к .post
:
Однако, решение с отдельным элементом .blog__item
, оборачивающим .post
более изящное и логически правильное
Элемент — часть блока, отвечающая за какой-то его функционал.
Элемент не может существовать вне блока. Вне блока он теряет смысл. Например, кнопка Найти
в форме поиска. Или ссылка в меню.
В имени элемента должно быть название блока и название элемента. В HTML элементы могут быть вложены друг-в-друга:
Элемент menu__link
принадлежит блоку menu
, об этом нам сообщает его класс.
Не верно писать menu__item__link
, потому что ссылка принадлежит меню, а не элементу menu__item
. Элемент не может принадлежать другому элементу. Элементы — это атомарные, неделимые части блока. Нам не нужно в БЭМ отражать вложенность тэгов в HTML.
Если вам кажется, что нужно сделать элемент у элемента, значит нужно либо создать новый блок, либо сделать ваше БЭМ-дерево с одинарной вложенностью элементов. Не нужно бояться создавать новые блоки.
В LESS такая структура выглядит так:
Плоская структура, без каскада. Все понятно, легко поддерживать.
Еще пример правильного именования классов:
Стили для мобильных пишутся внутри блоков и элементов. Код должен быть написан так, чтобы мы могли скопировать CSS-код блока (.menu
и то, что внутри него), вставить его в другой проект и все заработает. Не нужно их разносить по разным местам.
Так делать не правильно:
Разнося стили для одного блока по разным местам мы нарушаем главный принцип хорошей разработки: Don't repeat yourself. Это ухудшает поддерживаемость кода. Вася напишет стили для разных медиа, а Петя это может и не заметить.
Медиа-запросы, написанные внутри блоков и элементов обеспечат поддерживаемый, не повторяющийся код:
Изменить блок можно добавив к нему модификатор. Элементы блока могут быть переопределены по каскаду от модификатора, это нормально:
Изменить элемент так же можно через модификатор. Например, активный пункт меню:
Нужно быть внимательным. Если нужно сделать активной ссылку — подойдет модификатор. Если нужно сильно переделать элемент, то может быть лучше ввести новый блок. Не нужно каскадом переписывать все правила.
Такой подход позволяет:
проще добавлять/удалять классы в JS
отделить JS от HTML: при изменении имени блока в HTML не нужно будет менять JS
Если нужно сделать класс, который будет иметь постоянный набор стилей, которые нужно применять к разным блокам/элементам, то будет просто блок, который можно примиксовывать куда нужно. Например, .hidden
.
Если мы модифицируем состояние элемента в JS (активная ссылка, текущий пункт меню, текущий таб и пр.), то допускается применить модификатор без указания неймспейса: _active
.
Если же нужно модифицировать вид блока (форма поиска полная и сокращенная, меню в хедере и футере), то нужен модификатор с полным именем: .menu_top
В оригинальном БЭМ принято указывать, что именно меняет модификатор: &_size_big
, &_color_red
, &_type_warning
и пр.
Мы опускаем тип модификатора: &_big
, &_red
, &_warning
. Этого хватает, так проще.
Для текста, который вводит пользователь, нормально делать каскад по HTML-элементам:
Независимый блок или просто блок, это самодостаточный элемент страницы, который при перемещении в другое место на странице или на другую страницу не теряет своей самодостаточности (выглядит так же, работает так же). Харисов:
TODO: js-
навешивать на все задействованные элементы или только на те, на которые непосредственно навешаны обработчики?
Нет классов вне блоков. Все стили написаны только в контексте блоков. История создания БЭМ:
Модификаторы состояния блока/элемента, которые задействованы в JS нужно использовать без названия блока/элемента ():
При этом важно, чтобы стили не были заданы без отрыва от блока. Модификатор без блока :
Не допускается писать стили для _active
вне контекста модифицируемого блока или элемента. В БЭМ