По мотивам обсуждения в твиттере с подачи @delaz, решили написать пост и собрать еще вопросов и тем для разъяснения, которые в будущем планируем добавить в раздел про методологию.
Что у нас получилось?
- БЭМ — это не про префиксы.
- БЭМ — это не про длинные названия классов.
- Нельзя использовать элементы элементов в нейминге.
- Миксы.
- БЭМ не запрещает каскад (но и не приветствует).
- Как понять, когда делать блок, а когда — элемент.
Пойдем по порядку, а вы задавайте вопросы или дополняйте список в комментариях. Ответы будем выносить в пост.
«БЭМ — это длинные имена классов. b-button — это длинно!»
Отвечаем: БЭМ не навязывает префиксы. Чтобы писать хорошо поддерживаемый и реиспользуемый код, префиксы совершенно не нужны. Исторически они появились в переходный период для того, чтобы отличать новый код, написаный по БЭМ, от старого. Со временем мы от них отказались. Если посмотреть в код, можно увидеть, что префиксов там нет.
«А как же префикс js- или i- для блоков без визуального представления?»
Когда-то можно было сверстать сайт практически без JS-кода. Сейчас большая часть блоков имеет JS-представление. Глядя на текущую ситуацию мы поняли, что нет нужды как-то отличать технологии реализации блока на уровне нейминга. Посмотрите, к примеру, на Web Components. Объективно необходимости в префиксах нет, но каждый волен выбирать, что ему, проекту или команде удобнее и/или привычнее.
«Префиксы — ладно. А просто длинные названия блоков?»
В этом и похожих случаях вы можете использовать классы типа btn
вместо button
. Это никак не помешает вам разрабатывать по БЭМ.
Все как с названием переменных в JS: context
, ctx
или c
. Но помните, вам, вашим коллегам и тем, кто будет разрабатывать и поддерживать проект после вас это предстоит читать, понимать и с этим работать ;)
Остается вопрос про неймспейсы — мы пишем имя блока перед именем элементов (button__icon
) и модификаторов (button_active
).
Если декомпозировать «проблему», можно выделить 2 потенциальных минуса:
- Результирующий код, который приходится гонять по сети, весит больше. Тут на помощь приходит
gzip
, который отлично жмет повторяющиеся последовательности и сводит минус на нет. - Приходится больше кнопок нажимать на клавиатуре. Здесь помогают автокомплит в редакторе и инструментарий, который автоматически добавляет префиксы (CSS-препроцессоры и шаблонизаторы). Когда вы видите эти два минуса и больше ничего кроме них, мы предлагаем подумать и выбрать, что важнее — время, необходимое на нажатие клавиш, или время, затраченное на обдумывание архитектуры. Во втором случае БЭМ как раз очень сильно помогает. А первое легко автоматизировать не в урон проекту.
И тогда, благодаря неймспейсам, будет решено следующее:
- Исчезнет опасность случайно «задеть» внутреннее устройство блока. Мы получим аналог скоупа в JS, но для CSS. Для этого в Web Components придумали Shadow DOM, но в действительности простого добавления имени блока достаточно, чтобы получить тот же результат без лишних телодвижений.
- С первого взгляда на любой класс, хоть в CSS, хоть в HTML, хоть в любой другой технологии реализации блока, мы тут же поймем, к какому блоку он относится и какую задачу решает. Сравните:
active
(на что повлияет этот класс?) VS.input_active
илиitem
VS.nav__item
.«Что вы скажете на
nav__item__link
? Все равно длинно»
Да. И к тому же не по БЭМу :)
Неймспейсом служит только имя блока. А отражать вложенность в именах элементов не нужно. Это не только длинно, но еще и не позволит при повторном использовании блока в другой ситуации (или просто при рефакторинге) легко вынуть один элемент из другого. Плоская структура касается не только нейминга, но и расположения кода на файловой системе:
nav/
__item/
nav__item.css
__link/
nav__link.css
Для выражения вложенности вполне достаточно DOM-дерева:
<ul class="nav">
<li class="nav__item">
<a class="nav__link"></a>
</li>
</ul>
«Логично. Как отличить — делать блок или элемент? Например, nav__link
— это элемент меню или самостоятельный блок link
, который будет использоваться и в других местах на странице?»
Тут нам на помощь приходят миксы — возможность смешать на одном DOM-узле несколько блоков (или элементов/модификаторов) одновременно.
Предыдущий пример вполне может выглядеть так:
<ul class="nav">
<li class="nav__item">
<a class="link nav__link"></a>
</li>
</ul>
При этом все общее, что есть у всех ссылок на проекте, будет описано в блоке link
, а особенности, присущие только ссылке внутри nav
— для nav__link
.
«Почему нельзя стилизовать ссылки каскадом через .nav__item .link
?»
- Это несемантично. А если там в будущем окажется не ссылка вовсе?
- Во-вторых, это влечет за собой дополнительные сложности, ведь каскад затронет и вложенные сущности. Например, если в будущем вы захотите усложнить архитектуру или добавить выпадающее меню.
- Наконец, структура может поменяться и
nav__item
совсем исчезнуть.«Получается, что в каскадных таблицах стилей нельзя использовать каскад?»
Можно. Но нужно понимать, какие последствия это влечет.
Например, каскад уместен, чтобы менять элементы в зависимости от состояния блока (.nav_hover .nav__link { text-decoration: underline; }
) или, скажем, темы (.nav_theme_islands .nav__item { line-height: 1.5; }
).
Но в случае использования каскада вы рискуете повысить связанность кода и сделать его реиспользование невозможным, что в будущем может привести к ситуации, когда переписать проще, чем исправить.
«Как в принципе отличать, где блок, а где элемент?»
Если хочется переиспользовать кусок кода вне контекста родителя — это точно блок. Если кусок кода не имеет смысла без родителя — это скорее всего элемент. Аналогией служит Shadow DOM в Web Components.
Исключением может быть ситуация, когда у такого элемента оказывается слишком богатый внутренний мир и возникает желание сделать его собственные элементы. Тогда это можно представить как служебный «приватный» блок.
Что скажете? Наши ответы помогут вам разобраться в ваших ситуациях или же нам стоит подумать над чем-то еще? Будет очень здорово получить от вас вопросы или же примеры, которые мы сможем объяснить. А потом вынести всю эту полезную информацию в раздел сайта.
Очень ждем ваших комментариев!
Даже не знаю, напоминает ли этот текст мне что-то или нет...
Я подробнее хотел бы почитать про тему «БЭМ — это не только про CSS» :)
Тогда это можно представить как служебный «приватный» блок.
Можно уточнить что это за «приватный» блок и как это выглядит в коде?
Принципиально отличий нет. Примером такого блока в bem-components является menu-item — он не имеет смысла без
menu
, но сам по себе достаточно сложный, чтобы вынести его в отдельный блок. При желании можно ввести какой-нибудь префикс, чтобы отличать такие блоки.@voischev и про это напишем обязательно
Опечатка в примере про каскад:
.nav__hover
=>.nav_hover
Можно еще написать, что: 1) элементы не должны болтаться в дереве на пределами родителя (когда, например, к блоку миксуются чужие элементы) 2) не должно быть классов, где блок указан только с модификатором (пример: https://github.com/crushlovely/skyline/blob/master/example-forms.html#L96)
@mishanga
Исправил, спасибо!
Пост дополню.
Как обещал, дополняю про
block__el__el
:Если вам нужно сделать элемент у элемента, значит вам нужно создать новый блок или сделать ваше BEM-дерево с одинарной вложенностью элементов! Вместо:
Есть два варианта:
№1. Бить на блоки: делать новый блок
№2. Рубить ветки у вашего BEM-дерева: делать BEM-дерево с 1-ой вложенностью элементов
Типичная ошибка: Попытка вложить имя элемента в имя блока. Чтоб "схитрить" и "как-будто не вложить", написать не
block__el1__el2
аblockel1__el2
илиblock__el1el2
. Так нельзя.Будут проблемы при переносе
Такие имена можно делать только если .blockelem сохранит логический смысл при переносе в другой блок.
Обратите внимание - вы не можете вкладывать элементы в элементы в CSS, но можете и должны вкладывать элементы в элементы в HTML! DOM-дерево и BEM-дерево могут быть разными.
БЭМ-дерево на то и дерево, что поддерживает вложенность, поэтому в БЭМ-дереве, разумеется, разрешается вкладывать элементы в элементы, блоки в блоки, блоки в элементы. Запрет есть исключительно про нейминг.
element__element
нельзя в CSS, но можно в HTML!@delka Во втором примере потерялась чиселка возле имени блока.
Еще есть претензия к последнему абзацу. БЭМ-дерево на то и дерево, что поддерживает вложенность, поэтому в БЭМ-дереве, разумеется, разрешается вкладывать элементы в элементы, блоки в блоки, блоки в элементы. Запрет есть исключительно про нейминг.
@tadatuta Поправил бы ;-) Ты ж овнер!
@tadatuta спасибо, отредактировал и дополнил коммент твоим чтоб было понятней. Заодно и слайды доклада обновил: http://delka.github.io/talks/wsd/2014/bem/
@tadatuta А как к вам в бэмовый slack попасть?
@kompolom просто регишся под своим мылом тут: https://web-standards.slack.com/messages/bem/ Там доступ открыт для всех.