Следуете ли вы БЭМу, и насколько он востребован вне Яндекса?
БЭМ расшифровывается как: блок, элемент, модификатор. Это такой набор абстракций, на который можно разбить интерфейс и разрабатывать всё в одних и тех же терминах. Как говорит сайт bem.info, БЭМ предлагает единые правила написания кода, помогает его масштабировать и повторно использовать, а также увеличивает производительность и упрощает командную работу.
Круто, да? Но зачем вам масштабируемость и командная работа, если вы один верстальщик на проекте, который не претендует на популярность Яндекса? Чтобы ответить на этот вопрос, нужно отмотать историю лет на 10 назад, когда это подход только начали формулировать.
В основу того, что мы сейчас называем БЭМом, легла идея независимых блоков, которую Виталий Харисов сформулировал и презентовал в 2007 году на первой российской конференции по фронтенду. Это было настолько давно, что тогда даже слова «фронтенд» ещё не было, тогда это называлось клиент-сайд.
Идея была в том, чтобы ограничить возможности CSS для более предсказуемых результатов. Использовать минимум глобальных стилей и каждый отдельный элемент страницы делать блоком со своим уникальным классом и стилями, которые полностью его описывают. Селекторы по элементам и ID, хрупкие связки вложенности — всё это заменялось на простые селекторы по классам. Каждый класс в стилях — это блок. Благодаря этому блоки можно легко менять местами, вкладывать друг в друга и не бояться конфликтов или влияния.
#menu ul > li {
color: old;
}
.menu-item {
color: bem;
}
Потом появились абсолютно независимые блоки (АНБ), где у элементов внутри есть свой префикс с именем родителя, а состояния блоков снова дублируют класс, но уже с постфиксом. Подход обрёл черты современного БЭМа, одна из которых — многословность классов.
.block {}
.block_mod {}
.block__element {}
.block__element_mod {}
Эта многословность гарантирует уникальность элементов и модификаторов в рамках одного проекта. За уникальностью имён блоков вы следите сами, но это довольно просто, если каждый блок описан в отдельном файле. Глядя на такой класс в HTML или CSS, можно легко сказать, что это, и к чему оно относится.
Изначально АНБ, а потом и БЭМ, решали задачу важную для вёрстки любых масштабов: предсказуемое поведение и создание надёжного конструктора. Вы же хотите, чтобы ваша вёрстка была предсказуемой? Вот и Яндекс тоже. Ещё это называется «модульность» — о ней хорошо написал Филип Уолтон в «Архитектуре CSS», ссылка на перевод в описании.
Через пару лет в Яндексе окончательно сформулировали нотацию БЭМ. Любой интерфейс предлагалось разделять на блоки. Неотделимые части блоков — элементы. У тех и других есть модификаторы.
<ul class="menu">
<li class="menu__item"></li>
<li class="menu__item menu__item_current"></li>
</ul>
Например, блок поиска по сайту. Он может быть в шапке и в подвале — значит это блок. В нём есть обязательные части: поле поиска и кнопка «найти» — это его элементы. Если поле нужно растянуть на всю ширину, но только в шапке, то блоку или элементу, который отвечает за это растягивание, даётся модификатор.
Для полной независимости блоков мало назвать классы правильно или изолировать стили, нужно собрать всё, что к нему относится. В проекте по БЭМу нет общего script.js или папки images со всеми картинками сайта. В одной папке с каждым блоком лежит всё, что нужно для его работы. Это позволяет удобно добавлять, удалять и переносить блоки между проектами. Потом, конечно, все ресурсы блоков при сборке объединяются в зависимости от задач проекта.
Когда БЭМ вышел за пределы Яндекса, сначала его воспринимали как магию и старались воспроизводить дословно, вплоть до префиксов b-
у каждого блока. Такой смешной карго-культ.
.b-block {}
.b-block--mod {}
.b-block__element {}
.b-block__element--mod {}
Некоторые неверно понимали идею и появлялись даже элементы элементов. Потом за нотацию взялись энтузиасты и появилась одна из самых популярных: блок отделяется двумя подчёркиваниями, а модификатор — двумя дефисами. Наглядно и просто — сам так пишу.
.block {}
.block--mod {}
.block__element {}
.block__element--mod {}
А зачем вообще все эти нотации — я ведь один верстальщик на проекте, помните? Помню. А ещё помню, как сам верстал до БЭМа: в каждом проекте придумывал что-нибудь такое новенькое. А потом открывал код годичной давности и удивлялся — какой идиот это написал?
Возьмём, к примеру, русский язык. Мы же пишем с прописной имена людей, названия и прочее такое? Пишем. Чтобы легко потом считывалось: это Надя или надежда на лучшее? Уж не говоря про знаки препинания и другие полезные договорённости. Вот буква «ё», например, смягчает… так, о чём мы? Да, БЭМ.
До БЭМа были проекты с портянками стилей, которые нельзя трогать. Они копились годами, слой за слоем, как обои в древней коммуналке. Их просто подключали первыми, а потом перезаписывали что мешало. Когда у вас есть div { font-size: 80% }
— это даже уже не смешно.
/* Не смешно */
div {
font-size: 80%;
}
БЭМ продолжил развиваться в Яндексе и вырос за пределы вёрстки: появились уровни переопределения, богатый инструментарий, JS-библиотека для работы с БЭМ-классами, шаблонизаторы и целый БЭМ-стэк. БЭМу даже жест придумали, но это совсем другая история, специфичная для Яндекса.
Были и другие методологии: OOCSS Николь Салливан, SMACSS Джонатана Снука, вариации БЭМа и целые фреймворки. Даже у нас на продвинутом интенсиве можно было выбрать любую методологию для вёрстки учебного проекта.
Но выжил только БЭМ и его вполне можно назвать стандартом де-факто современной разработки интерфейсов. И что — это финальная ступень эволюции? Нет конечно. Как ни автоматизируй, многое в БЭМе приходится делать руками, и возможны конфликты.
Модульность и изоляцию стилей блока можно решить ещё лучше. Есть веб-компоненты, CSS-модули, куча решений CSS-в-JS и даже, прости господи, атомарный CSS. Но нет единого решения накопившихся проблем на следующие 10 лет, каким когда-то стал БЭМ. Что это будет? Посмотрим. Я ставлю на веб-компоненты.
А пока, если вы опишете интерфейс по БЭМу — код будет организован, предсказуем, расширяем и готов для повторного использования. Не только для вас, но и для других.
Видеоверсия
Вопросы можно задавать здесь.
Комментарии (41)
Varim
20.09.2017 19:44блок отделяется двумя подчёркиваниями, а модификатор — двумя дефисами.
.block {} .block--mod {} .block__element {} .block__element--mod {}
Элемент, а не блок, отделяется двумя подчеркиваниями?
Может я не прав, но поскольку определяется модификатор «block__element--mod», то возможно правильно написать — "имя элемента отделяется двумя подчёркиваниями, а имя модификатора — двумя дефисами"
https://ru.bem.info/methodology/quick-start/#Модификатор
В документации, в примерах, пишут без дефиса.
Меньше путаницы — легче новичкам.
Лучше бы либо доку переписать либо в учебных статьях подправить.
Подскажите, в «search-form search-form_focused», имя модификатора это «search-form search-form_focused» или «search-form_focused»?Costigans
21.09.2017 11:46В данном примере названием модификатора выступает «search-form_focused».
korovnikiss
21.09.2017 15:48БЭМ не разделители и подчеркивания. Разделять можете как Вам нравится. Главное показать отношение элемента к блоку и их модификаторы.
Reey
20.09.2017 19:57+14Для меня "ave" моментом помню стало, когда мне объяснили, что не обязательно соблюдать иерархию ноды. Это вообще, как я понял, один из самых путающих моментов для новичков.
Раньше я думал, что БЭМ это:
<div class="note"> <div class="note__sidebar"> <div class="note__sidebar__title"> <img class="note__sidebar__title__icon"/> </div> </div> </div>
И думал что это какая-то фигня, а потом я понял, что надо так:
<div class="note"> <div class="note__sidebar"> <div class="note__title"> <img class="note__icon"/> </div> </div> </div>
И я прозрел
Headmaster11
22.09.2017 11:37Теперь и я прозрел. Спасибо.
Удивительно, как такой лаконичный пример никто раньше не привёл
Shifty_Fox
22.09.2017 23:51Я тоже в свое время прозрел на этот счет
Отдельно добавлю, что если все таки нужно положить элемент в элемент, то для этого можно использовать одинарное подчеркивание. В связи с этим и удобно использовать именно — как разделитель для модификатора:
note__sidebar_title--size_mvithar
23.09.2017 09:42Мы для отделения слов внутри имени используем -, а не _
Shifty_Fox
24.09.2017 17:28Тогда все сольется в один большой элемент
front-page__section-promo-subinfo-title
против
front-page__section-promo_subinfo-title
или даже
front-page__section-promo_subinfo_title
Необходимость возникает когда title в блоке действительно не один, а делить все на микроблоки уже слишком
jakobz
21.09.2017 02:29> Если поле нужно растянуть на всю ширину, но только в шапке, то блоку или элементу, который отвечает за это растягивание, даётся модификатор.
А мы не так делаем. Мы разделяем «внешние свойства» — все что за border-ом — типа padding, position, width; и «внутренние свойства» — все остальные. Блок верстается резиновым, и не задает сам «внешние» свойства. А его потребителям — позволено невозбранно навешивать на чужой блок селекторы своих элементов — которые его ставят как им надо. Очень удобственно. Ну типа так:
<div class='my-panel'> <!-- .my-panel { display: flex } --> <!-- .my-panel__item { flex: 1 1 auto; margin: 10px; } --> <div class='my-panel__item ui-button'> <div class='ui-button__caption'>HI</div> </div> </div>
Ну и CSS Modules пробуем. Есть и плюсы, и минусы.Varim
21.09.2017 06:59навешивать на чужой блок селекторы своих элементов
Не могли бы пояснить что это значит, JavaScript пишет типа block.className += " button_selector"?
Или копируется разметка, затем верстальщик ручками вписывает нужные селекторы?
Может вы о чем то другом.jakobz
21.09.2017 11:02Суть не в том, как именно class навешивается. Суть в том, что есть договоренность:
— компонент (блок) сам себе не задает margin/position/flex/width
— компонент верстается так, чтобы если его растянули — он бы нормально выглядел
— потребитель компонента сам вешает ему margin/flex/и т.п., чтобы его как нужно вставить
Т.е. если у нас кнопочка — она без паддингов и резиновая. Если кнопочку надо вставить и, скажем, растянуть пошире и отодвинуть справа — мы на нее вешаем flex и padding снаружи. Можно классом, можно инлайном — тут не суть. Это, вроде как, не чистый BEM, но нам так сильно проще жить.vithar
23.09.2017 09:44Это чистый БЭМ, называется миксы и так и рекомендуется делать в документацмм :
Fibril
21.09.2017 04:00+1Благодаря этому блоки можно легко менять местами, вкладывать друг в друга и не бояться конфликтов или влияния.
Это надуманная проблема, в реальной разработке в такой гибкости нет необходимости. Те элементы, у которых позиция в разметке может измениться, должны верстаться с учетом возможных изменений и без всяких БЭМ. Среди остальных элементов в любом случае будут элементы, изменение позиции которых потребует изменения стилей, это уже вопрос дизайна и БЭМ здесь ничего не решает.anttoshka
21.09.2017 09:43БЭМ решает проблему того, что в процессе разработки вдруг необходимо на страницу добавить блок, который есть на соседней. А этот блок разработчик мог написать с селектором типа .my-page .my-block и в итоге нужно или на новой странице добавлять класс .my-page, что может дать проблемы или оставлять в стилях только .my-block, но тогда его стили могут уже как-то переопределяться в будущем. Разработка же не заканчивается на этом.
Кроме того одним махом решается вопрос поддержки — намного проще изменить внутри блока какой-то элемент зная, что он точно изменится везде, а не нужно перепроверять все страницы, где этот элемент может быть.W3C
21.09.2017 11:46Блоки, которые могут использоваться ну других страницах или еще где-то должны верстаться без зависимостей в любом случае. В рамках спецификации можно найти много изящных способов, как сверстать элемент полностью самостоятельным и независимым. БЭМ здесь ничего не решает, только уродует и ограничивает.
Shifty_Fox
22.09.2017 23:56БЭМ задает удобные и простые правила как верстать _все_ компоненты полностью самостоятельными и независимыми из коробки
jt3k
21.09.2017 11:46Это не надуманная проблема если в твоей жизни была фирма с общим фирменным стилем на всех сайтах.
neurocore
21.09.2017 11:46Пока что единственные 2 момента меня останавливают от использования БЭМа:
1. Трудно читаемый код с такими длинными классами (в частности html-код)
2. Нет проектов с более, чем 2-мя верстальщикамиjakobz
21.09.2017 15:42-1Там тулинг же надо, руками это писать — мазохизм. В LESS и прочих — дублирование убирается через вложенность правил. А в шаблонах или коде — тоже придумываются хелперы. Скажем, в реакте у нас:
const b = new Bem('my-button'); // один раз вверху модуля
…
b.div({ element: 'caption', mods: [isRed && 'red'] }); // my-button__caption my-button--red -->
У нас нет выделенных верстаков вообще, разработчики всё делают — и JS, и верстку. И всем удобно. Правда, мы уже теперь на CSS-модули переезжаем — они фичи БЭМ-а перекрывают, но суть та же.
igontarev
21.09.2017 11:46+1идея бема хорошая, а реализация неудобная, вот почему бы не договориться, что модификаторы должны содержать глаголы is, has, и не использоваться самостоятельно, тогда можно писать типа
.button.is-disabled {}
<button class="button is-disabled" />
вместо
.button--disabled {}
<button class="button button--disabled" />
Aingis
21.09.2017 13:46DOM-элемент может представлять собой несколько БЭМ-сущностей (миксы). Например, это может быть одновременно элементы раскладки (
page__elem
), ответственный за размещение (position
,margin
,flex
— все дела) и непосредственно какой-то блок (block
). В таком случае ваши модификаторы-классы могут создавать коллизии. Но если избавиться от миксов, то можно и так. Правда, в таком случае вы увеличите уровень вложенности, и не исключены сложности с раскладкой, там где важен уровень вложенности.
(P.S. А ещё так нельзя было делать в IE6 — во времена, когда создавался БЭМ.)
Kasheftin
21.09.2017 17:12+1Зачем в модификаторе повторять всю цепочку названия? Моя верстка в общем следует бэму, только все модификаторы, и только они, начинаются с `-` т.е. вместо
идет просто<button class="search__button search__button--primary">
<button class="search__button -primary">
anttoshka
22.09.2017 22:57Эта проблема уходит вместе с использованием jade.
Код по типу
+b.block
+e.element_mod
преобразуется в
<div class='block__element block__element_mod>
БЭМ совместно с препроцессорами очень удобно использовать.
GoodGod
22.09.2017 11:37А как вы относитесь к css-modules? Ничего придумывать не надо, простые названия, но никогда не пересекутся в двух компонентах и стек технологий чтобы использовать уже есть.
vintage
22.09.2017 12:14Некоторые неверно понимали идею и появлялись даже элементы элементов.
Когда элемент сам по себе является блоком с элементами внутри и нужно их стилизовать, то тут как нельзя кстати приходятся "элементы элементов":
[block1] {} [block1~="mod"] {} [block1_element1] {} [block1_element1~="mod"] {} [block2] {} [block2~="mod"] {} [block2_element2] {} [block2_element2~="mod"] {} [block1_element1_element2] {} [block1_element1_element2~="mod"] {}
Более живой пример:
[header]{} [header~="big"]{} [header_query]{} [suggest] {} [suggest_option] {} [suggest_option~="first"] {} [header_query_option~="first"] {}
Ну и плюс ваши "уровни переопределения", только без лишней абстракции:
/* Первая подсказка в поле поиска на главной странице */ [serp_header_query_option~="first"] {}
Фактически получается каскад, но не на уровне ДОМ, а на уровне дерева блоков, и без проблем со специфичностью. Сами компоненты в доме могут располагаться друг относительно друга как угодно. Например, suggest может рендериться в body.
Как ни автоматизируй, многое в БЭМе приходится делать руками, и возможны конфликты.
Мы БЭМ классы генерируем автоматически на основе:
- Имени блока (mol_button, например).
- Имени блока в контексте другого блока (блок mol_button, например, может иметь имя option в контексте mol_select, что даст селектор mol_select_option).
- И так далее по дереву компонент.
Пример из жизни:
<input id="$mol_app_todomvc.Root(0).Task_row(1).Title()" mol_app_todomvc_task_row_title mol_string mol_view placeholder="Task title" >
[mol_view] { /* Общие стили для всех блоков, на этот селектор можем повесить всякие css-reset */ } [mol_string] { /* Стили для блоков ввода строки текста */ } [mol_app_todomvc_task_row_title] { /* Стили для блоков ввода текста задач в конкретном ToDoMVC приложении */ }
vintage
22.09.2017 12:20Ну а в "шаблоне", конечно, никакого БЭМ-а:
<= Title $mol_string hint <= title_hint @ \Task title value?val <=> title?val \
vithar
23.09.2017 09:54+1Это не БЭМ. Ключевое отличие БЭМ от любого другого именования с кучей подчёркиваний — можно в любой момент программно определить что есть блок, что элемент, а что модификатор в любом произвольном идентификаторе.
vintage
23.09.2017 11:02-1Я вам предлагаю сделать шаг вперёд и понять, что элемент — это тоже блок, только в пространстве имён другого блока.
vithar
23.09.2017 11:11+2В этом нет смысла. Текущая методология достаточно обрезана бритвой Оккама, чтобы быть способной выразить всё, что нужно.
vintage
23.09.2017 11:36Можно ещё немного обрезать и увеличить выразительность. Ваш текущий подход без "элементов элементов" работает для целиковых страниц, где вы можете руками приписать любой класс к элементу на любой глубине. Но совершенно не работает для компонент (у каждого из которых свой шаблон), ибо вы не можете так просто добавить произвольный класс элементу в шаблон подкомпонента.
Aingis
На картинке Вадим смотрит на тебя с поднятыми бровями так, как будто ты не используешь БЭМ.