Привет! Уже довольно давно ломаю голову, как совокупить БЭМ-подход с регулярно возникающей ситуацией, когда где-то внутри блока нужно разместить один или несколько блоков с тем же названием.
У нас такая практика: есть блок, он описывает DOM-структуру, и к нему через модификатор можно цеплять один из заданных стилей для "раскраски". Например, меню:
<ul class="menu menu_style_main">
<li class="menu__item"><a href="#" class="menu__link">One</li>
...
</ul>
И стили:
.menu_style_main .menu__link {
font-size:20px;
color:#F00;
}
...
.menu_style_alt .menu__link {
font-size:12px;
color:#666;
}
То есть используем разрешенный методологией каскад "блок-с-модификатором - элемент".
Приключения начинаются, когда дизайнер рисует меню, в котором верхние пункты расхлопываются в попап, внутри которого свой маленький мир, как назло включающий пару вложенных меню. Вот картинка (с первого попавшегося сайта), иллюстрирующая идею: http://joxi.ru/1A5pxGZfK6BoB2
В этом случае селектор, выбирающий пункты главной горизонтальной менюшки, поймает и пункты вложенных вертикальных. Кто-нибудь знает более-менее цивилизованный способ, как с этим бороться?
Я вижу такие варианты:
- Явно описывать в CSS структуру блока, используя "стрелку":
.menu_style_main > .menu__item > .menu__link
- вроде не труЪ, привязываемся к структуре DOM, вынуждаем себя ее помнить и суппорить изменения в 10 местах. - Спускать модификатор вниз на элементы, то есть вешать на все элементы блока правильный модификатор стиля:
.menu__link_style_main
etc. Если действовать в таком духе (учитывая, что проблема относится к любому модификатору блока), получим что-то типа pyramid of doom, но в стиле БЭМ - когда 90% html-кода расположены в атрибутеclass
. Vermicelli of BEM. Как минимум работа со вкладкой Elements консоли разработчика станет очень грустной. - Считать, что это не один блок, а разные (по сути тупо переносим название стиля в имя блока):
.menu-main .menu-main__link {...}
Во-первых, похоже на предыдущий вариант (приклеиваем название стиля ко всем элементам, только не как модификатор, как часть имени блока). Во-вторых, это все-таки один блок - семантика та же, поведение для JS то же, выглядит только иначе. - Сделать, чтобы вложенное меню на самом деле не было вложенным на уровне DOM (положить рядом и спозиционировать). Неудобно и не реализуемо для других случаев.
- Забить на все и тупо инлайнить стили прямо в HTML. Не канает, мы не SPA, собирать это все придется на сервере (а там PHP), и потом еще как-то дублировать поведение на клиенте.
Другие примеры "рекурсивности":
- блок "таблица" с элементами-ячейками (для рисования лейаутов, внутри ячейки может возникнуть новая "таблица")
- блок "персона", внутри показываются друзья (тоже "персоны")
- блок "сообщение на форуме", внутри текста может быть процитировано другое "сообщение на форуме"
(Вообще мы делаем конструктор, где юзер сам двигает блоки, и в теории вообще что угодно может оказаться внутри чего угодно).
Кто что посоветует, кто сталкивался с подобными траблами? Сейчас мы используем вариант №1 (селектор прямого потомка), и я склоняюсь к варианту №2 (перенести модификатор на элементы). Потому что зеркалить dom-структуру в CSS приходится руками, а развесить модификаторы мы можем автоматом. И второй вариант вроде бы валиднее с точки зрения методологии.
Расскажите ваши мысли, и может я что-то вообще упускаю? Спасибо )
Универсальной серебряной пули для этого случая нет, каждый кейс стоит разбирать отдельно.
В конкретной описанной ситуации с меню я бы остановился на первом варианте.
Для случая
решение может быть таким: в блоке
layout
остается только общий универсальный код, а отличия выносятся в миксы (например,<table class="layout user-layout">
).по-идее такие персоны должны бы и выглядеть одинаково. Но если они выглядят по-разному, то постараться добиться того, чтобы родительский блок был без модификатора, тогда вложенный «победит» по весу селекторов (либо просто гарантировать порядок селекторов в CSS). Аналогично и с сообщением внутри сообщения.
Но вообще, справедливости ради, стоит отметить, что ситуация, когда блок а) вложен сам в себя б) при этом вложенный выглядит иначе и в) выглядит иначе не благодаря самому факту вложенности — это все-таки достаточно редкий кейс.
Спасибо. Случай с персонами, строго говоря, придуманный, но допустимый. Выглядеть они могут похоже, но по-разному (вложенные мельче и у них меньше деталей, например).
Вес и порядок селекторов - это как раз то, от чего в первую очередь хочется избавляться. Вообще одна из основных причин, почему мы стали смотреть в сторону БЭМ - это нежелание думать о порядке подключения стилей =)
А разве так можно, чтобы "благодаря"? Идея же в том, чтобы контекст не влиял на внутренности блока. Или подразумевается, что про самого себя в качестве контекста блок может что-то знать?
P.S. С ru.bem.info/forum/ не получилось отправить и пост, и коммент тоже не хочет. Приходит 502 ответ с текстом "Backend unavailable". Так что пишу через Issues на гитхабе =)
Выносить отличия в миксы - в теории тоже вариант, но прямо сейчас мы не можем его пощупать, т.к. внедрение миксов "блок-блок" в верстку у нас плохо автоматизировано. Верстка делается примерно таким шайтаном:
Выдаст:
Соответственно, смиксовать элемент с блоком мы можем (так:
<li fx:e="item" fx:b="menu-item">
, но все вложенное уже будет считаться элементами нового блока. А блок с блоком - только руками, явно указывая классы, а это очень лень =( Ну и, ИМХО, одновременное сосуществование двух блоков усложняет восприятие.При этом "спускать" некоторые модификаторы с блока на элементы мы как раз можем автоматически. Я вот даже сужу вопрос: поругайте, пожалуйста, вариант №2, когда ко всем элементам добавляется общий модификатор стиля.
Полностью забить на порядок в любом случае не удастся. Мы для управления порядком используем отдельную технологию про зависимости (https://ru.bem.info/technology/deps/).
В целом да, но не нужно возводить рекомендации в абсолют. Если есть продуктовое требование, которое звучит как «вложенный пользователь отображается иначе», то в целом нет ничего плохого, если это будет именно так отражено в коде.
Общее правило в том, что потомки не должны знать про родителя. А. в данном случае блок пользователя знает, как выглядят вложенные в него блоки пользователя.
Делать так без явной необходимости — определенно оверхед, но для каких-то конкретных кейсов — нормально. Вряд ли «Vermicelli of BEM» случится от пары таких блоков на странице ;)
PS: спасибо за репорт про форум, будем исследовать.