Comments 81
блок отделяется двумя подчёркиваниями, а модификатор — двумя дефисами.
.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»?
Для меня "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>
И я прозрел
Удивительно, как такой лаконичный пример никто раньше не привёл
Отдельно добавлю, что если все таки нужно положить элемент в элемент, то для этого можно использовать одинарное подчеркивание. В связи с этим и удобно использовать именно — как разделитель для модификатора:
note__sidebar_title--size_m
Мы для отделения слов внутри имени используем -, а не _
front-page__section-promo-subinfo-title
против
front-page__section-promo_subinfo-title
или даже
front-page__section-promo_subinfo_title
Необходимость возникает когда title в блоке действительно не один, а делить все на микроблоки уже слишком
<div class="header">
<div class="header__button">
<div class="header__button-icon"></div>
Button
</div>
</div>
К слову, переиспользуемые компоненты — это тоже блоки, только они являются элементами другого блока («примешиваются» к нему). Например:
<div class="header">
<div class="button header__button">
<div class="button__icon"></div>
<div class="button__text">Button</div>
</div>
</div>
А дальше смело оформляйте через button все кнопки, а через header__button меняйте эту конкретную. Получается вполне классическое наследование.
Во втором примере как вы поменяете цвет иконки в кнопке в шапке, если "кнопка с иконкой" — отдельный переиспользуемый компонент со своим отдельным шаблоном?
.header__button .button__icon {
background: red;
}
Так?
Именно. Надо ли объяснять чем это плохо?
.header__button > .button__icon {
background: red;
}
Так тоже плохо?- Увеличивается специфичность селектора, а значит в дальнейшем перебивать его придётся не менее специфичным селектором и дублировать стили для увеличенной специфичности (всякие ховеры, фокусы и тп, например).
- При использовании пробела воздействует не только на элементы нужного нам блока, но и на элементы вложенных блоков того же типа, что зачастую является вредным и неожиданным влиянием.
- При использовании угловой скобки слишком жёстко завязывается на структуру вложенного компонента. При изменении структуры стиль будет отваливаться, что весьма не просто отслеживать.
Да нет, это проблемы из практики. Собственно для решения этих проблем БЭМ и появился. И он замечательно их решает в случае статических страниц. Для использования же в компонентах, нужно сделать ещё один шаг в том же направлении — нужен механизм "каскада", но на уровне компонент, а не дом-дерева. Пресловутый .header__button
— это каскад второго уровня. Но уровней бывает нужно больше и тогда БЭМ откатывается обратно до использования дом-каскада. А всего-то, нужно позволить многоуровневый каскад .header__button__icon
, но такие классы руками уже не попишешь — нужна автогенерация. У нас она есть. В случае bemhtml, на сколько я понял, есть только костыль с подсовыванием css-классов через данные.
значит в дальнейшем перебивать его придётся не менее специфичным селекторомЧто в этом плохого?
и дублировать стили для увеличенной специфичности (всякие ховеры, фокусы и тп, например)Так вроде бы не дублировать, а переопределять только некоторые свойства CSS?
Типа так:
.header__button .button__icon, .header__button .button__icon:hover {
background: red;
}
.header__button .button__icon:hover
{
font-size: 22;
}
Правда я уже несколько лет не писал CSS так что не уверен. Сейчас как раз освежаю знания.А что вы предлагаете? Вроде CSS специально так и работает.
2. При использовании пробела воздействует не только на элементы нужного нам блока, но и на элементы вложенных блоков того же типа, что зачастую является вредным и неожиданным влиянием.Так если один верхний блок отличается от другого верхнего блока, значит можно найти такой селектор который однозначно задает свойства только нужному элементу?
3. Пункт принят.
- Речь о такой фигне:
/* иконку делаем полупрозрачной */
.button__icon {
opacity: .8;
}
/* при наведении делаем её непрозрачной */
.button__icon:hover {
opacity: 1;
}
/* в шапке хотим ещё более прозрачную кнопку */
.header_button .button__icon {
opacity: .5;
}
/* чиним сломавшийся на предыдущем шаге ховер-эффект */
.header_button .button__icon:hover {
opacity: 1;
}
- Они могут быть вложены друг в друга даже. Ну это не про кнопки, конечно, а про списки, например.
ru.bem.info/methodology/css/#Принцип-открытостизакрытости
А мы не так делаем. Мы разделяем «внешние свойства» — все что за 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 пробуем. Есть и плюсы, и минусы.
навешивать на чужой блок селекторы своих элементовНе могли бы пояснить что это значит, JavaScript пишет типа block.className += " button_selector"?
Или копируется разметка, затем верстальщик ручками вписывает нужные селекторы?
Может вы о чем то другом.
— компонент (блок) сам себе не задает margin/position/flex/width
— компонент верстается так, чтобы если его растянули — он бы нормально выглядел
— потребитель компонента сам вешает ему margin/flex/и т.п., чтобы его как нужно вставить
Т.е. если у нас кнопочка — она без паддингов и резиновая. Если кнопочку надо вставить и, скажем, растянуть пошире и отодвинуть справа — мы на нее вешаем flex и padding снаружи. Можно классом, можно инлайном — тут не суть. Это, вроде как, не чистый BEM, но нам так сильно проще жить.
Примешивание блока к элементу. Таким способом удобно позиционировать блоки внутри других блоков.
з.ы. это было мое второе «прозрение», как говорят в комментах выше.
Проблема в том, что они ничего не говорят о состоянии блока / элемента, и по факту аналогичны внутренним стилям.
Когда вы пишете:
$elem.addClass('color-red');
— это абсолютно то же, что и
$elem.css('color', 'red');
И даже если вы добавите элементу состояние color-red как модификатор, это всё равно не очень хорошо: оно ни о чём не говорит.
$elem.addClass('elem_color-red');
Правильно делать вот так:
$elem.addClass('elem_warning');
Вот так всё становится простым и очевидным — класс переключает элемент в состояние warning. Если вы потом захотите, чтобы при warning текст не только становился красным, но ещё и мигал, вам не придётся бегать по всему js-файлу, правя класс на color-red-blink или elem_color-red-blink, а всего лишь исправить стиль у elem_warning.
(стоит сказать, что если делать elem_color-red, то тоже не придётся — БЭМ уже здесь начинает выполнять своё предназначение)
Благодаря этому блоки можно легко менять местами, вкладывать друг в друга и не бояться конфликтов или влияния.Это надуманная проблема, в реальной разработке в такой гибкости нет необходимости. Те элементы, у которых позиция в разметке может измениться, должны верстаться с учетом возможных изменений и без всяких БЭМ. Среди остальных элементов в любом случае будут элементы, изменение позиции которых потребует изменения стилей, это уже вопрос дизайна и БЭМ здесь ничего не решает.
Кроме того одним махом решается вопрос поддержки — намного проще изменить внутри блока какой-то элемент зная, что он точно изменится везде, а не нужно перепроверять все страницы, где этот элемент может быть.
Это не надуманная проблема если в твоей жизни была фирма с общим фирменным стилем на всех сайтах.
1. Трудно читаемый код с такими длинными классами (в частности html-код)
2. Нет проектов с более, чем 2-мя верстальщиками
const b = new Bem('my-button'); // один раз вверху модуля
…
b.div({ element: 'caption', mods: [isRed && 'red'] }); // my-button__caption my-button--red -->
У нас нет выделенных верстаков вообще, разработчики всё делают — и JS, и верстку. И всем удобно. Правда, мы уже теперь на CSS-модули переезжаем — они фичи БЭМ-а перекрывают, но суть та же.
Трудно читаемый код с такими длинными классами
Парадокс в том, что читать верстку, написанную с соблюдением БЭМ несравнимо удобнее. Стоит только начать, вернуться назад желания уже не возникнет.
Нет проектов с более, чем 2-мя верстальщиками
Это еще одно заблуждение. Даже одному писать код по единым понятным правилам намного проще, чем каждый раз придумывать новое решение, конфликтующее с предыдущим.
.button.is-disabled {}
<button class="button is-disabled" />
вместо
.button--disabled {}
<button class="button button--disabled" />
Я с бемом использую модификаторы — полет нормальный
<div class="menu__item button button_active">...</div>
а во-вторых, когда ты пишешь компонентный css с использованием например reactjs, то непонятно что вообще за компонент нам надо писать, нужна кнопка, делаешь компонент кнопки и стили для него, нужна менюшка, делаем компонент меню и описываем стили, но когда нужно написать что-то типа того кода что я выше написал, то тут или новый компонент, или новые модификаторы для меню или еще что-то, но никак не смешивание кнопки с меню
ps: я честно не пишу по бэму, и если бы мне попался уже написанный проект с кодом типа
<div class="menu__item button button_active">...</div>
то мне было бы сложно его поддерживатьЯ использую vuejs, и например маржен я могу задать через директиву, директива довольно функциональна, так же имеет модификаторы для медиа запросов
<menu>
<v-button v-margin.xs.top="1" v-margin.md.top="2">
send
</-v-button>
</menu>
второй очень важный момент, что каждый компонент имеет свой файл со стилями и при импорте вебпак добавлят к названиям классов хэш, что гарантирует уникальные названия классов, чтобы уж наверняка. В моих шаблонах вы никогда не увидите классы, любые стили должны приналежать компоненту и построение шаблонов строго теми средствами, которые предоставляются в проекте, сетка+хэлперы/директивы
Наверное, можно, но это сложнее.
<button class="search__button search__button--primary">
идет просто <button class="search__button -primary">
Код по типу
+b.block
+e.element_mod
преобразуется в
<div class='block__element block__element_mod>
БЭМ совместно с препроцессорами очень удобно использовать.
Некоторые неверно понимали идею и появлялись даже элементы элементов.
Когда элемент сам по себе является блоком с элементами внутри и нужно их стилизовать, то тут как нельзя кстати приходятся "элементы элементов":
[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 приложении */
}
Ну а в "шаблоне", конечно, никакого БЭМ-а:
<= Title $mol_string
hint <= title_hint @ \Task title
value?val <=> title?val \
Это не БЭМ. Ключевое отличие БЭМ от любого другого именования с кучей подчёркиваний — можно в любой момент программно определить что есть блок, что элемент, а что модификатор в любом произвольном идентификаторе.
Я вам предлагаю сделать шаг вперёд и понять, что элемент — это тоже блок, только в пространстве имён другого блока.
В этом нет смысла. Текущая методология достаточно обрезана бритвой Оккама, чтобы быть способной выразить всё, что нужно.
Можно ещё немного обрезать и увеличить выразительность. Ваш текущий подход без "элементов элементов" работает для целиковых страниц, где вы можете руками приписать любой класс к элементу на любой глубине. Но совершенно не работает для компонент (у каждого из которых свой шаблон), ибо вы не можете так просто добавить произвольный класс элементу в шаблон подкомпонента.
Можно пример?
Это позволяет не писать стили вида
.my-component__element .foreign-component__element
а писать
.foreign-component_my-style .foreign-component__element
Если вводить модификатор на каждое место использования, то каждый компонент будет представлять из себя помойку из кучи неортогональных модификаторов, каждый из которых используется только в одном месте и больше нигде.
В примере же у вас ничего принципиально не поменялось — как был каскад, так и остался. Давайте что-то более конкретное. Допустим у нас есть следующие компоненты:
- Абстрактная иконка (ya-icon) текущего цвета
- Абстрактная строка поиска (ya-search) с кнопкой перехода (ya-search__go ya-icon)
- Шапка портала (ya-header) со строкой поиска (bt-header__search ya-search) и меню выбора сервиса.
- Главная страница биткоин-сервиса (btc-morda) с шапкой (btc-morda__header), телом и подвалом.
Задача: перекрасить кнопку перехода в строке поиска в шапке на морде биткоин-сервиса в золотой цвет. Как вы это сделаете?
У меня-то нет помойки. Я просто напишу в /btc/morda/morda.view.css
:
[btc_morda_header_search_go] { color : gold }
Ок, не абстрактная, а конкретная иконка (ya-icon-search ya-icon). Если вопрос в том, зачем отдельный блок, то затем, чтобы через css можно было ею управлять. Например, чтобы текущий цвет шрифта был основным цветом иконки.
Вода камень точит :-) Впрочем, $mol я им даже не предлагаю. Хотя, было бы интересно послушать конструктивную критику, ведь он создан в том числе и для решения "портальных" проблем. Интересно, яндексоиды вообще заходили на страницу проекта, хотя бы просто посмотреть "как оно у них сделано", а то и стащить себе пару-другую свежих идей?
Вообще, забавно, как на базе идиомы непротиворечивого нейминга css-классов (прорывной для своего времени) эволюционно выстроилась целая экосистема для создания веб-приложений. Этакая геометрия Лобачевского: на базе чуть другого набора аксиом — совершенно другие результаты, довольно странные результаты. Для компонент куда естественней было бы использовать классы (Блоки) и свойства (Модификаторы — свойства, возвращающие примитивы, Элементы — свойства, возвращающие вложенные компоненты). Если проектировать сейчас (в 2017) "фреймворк от Яндекса" с чистого листа, думаю никакого БЭМ-а и в помине бы не было, как и зависимости от jQuery и кучи DSL-ей. Зато была бы статическая типизация и реактивное программирование.
Экосистема БЭМ сейчас стагнирует: морально устаревшие подходы, куча велосипедов, туча легаси-кода на них. Всё это ни переписать, ни отрефакторить, ни даже просто признаться, что последние 10 лет шли не туда. Яндекс имеет ресурсы и авторитет, чтобы двигать индустрию вперёд, а вместо этого побирается у Фейсбука, прикручивая его совершенно бестолковую поделку (React) к своему неконкурентноспособному велосипеду. Оно и понятно, ведь с подобным кодом в 2017 никто связываться не захочет:
// своя система модулей
// только динамическая типизация
modules.define(
'hello', // имя блока
['i-bem-dom', 'input'], // подключение зависимости
// функция, в которую передаются имена используемых модулей
// очень удобно, когда зависимостей десяток
function(provide, bemDom, Input) {
// декларация блока
// нам ведь всегда нужна асинхронная инициализация
provide(bemDom.declBlock('hello', {
// конструктор для описания реакции на события
// событийная модель, замаскированная под реактивную
onSetMod: {
'js': {
'inited': function() {
// любой вложенный элемент нужно сначала найти
// а если он изменится - не забыть обновить ссылку на него
this._input = this.findChildBlock(Input);
// DOM-событие, на которое будет реакция
this._domEvents().on('submit', function(e) {
// предотвращение срабатывания события по умолчанию:
// отправка формы на сервер с перезагрузкой страницы
e.preventDefault();
// к чёрту консистентность, инварианты, единый источник истины, слои абстракции
// херачим текст прямо в элемент
this._elem('greeting').domElem.text('Привет, ' +
this._input.getVal() + '!');
});
}
}
}
}));
});
Я же правильно понимаю, что приведенный сниппет кода должен ярко иллюстрировать ущербность БЭМа?
Она в том, что
1. Среди возможностей есть по-настоящему асинхронная во всех смыслах модульная система, позволяющая при необходимости точечно переопределять любые модули (стоит ли говорить, что ее использование опционально и БЭМ не перестанет быть БЭМом без нее?)
2. Помимо возможности декларативно описать реакцию на изменение модификаторов, можно воспользоваться и соответствующими событиями, которые обладают всеми возможностями нативных событий.
3. При необходимости отменить поведение события по умолчанию, нужно (о мой б-г!) вызвать
preventDefault()
.4. В hello-world примере не стали задвигать телегу про единый источник истины, докручивать флакс с привкусом редакса и прочие свистелки, которые каждый имеющий голову разработчик выбирает для себя сам и которые не имеют прямого отношению к тем вещам, за которые отвечает БЭМ.
Все верно? Ничего не упустил? :)
В JS уже есть стандартные модули — стоит использовать их. Отложенная загрузка легко реализуется через механизмы реактивного программирования, без необходимости превращать код модуля в лапшу из колбэков. Пример с догрузкой ваших Я.Карт по требованию. И, кстати, да, у вас слишком много "опционального" и "вариативного". В итоге, в ваших исходниках сложно разбираться и ещё сложнее понять "как писать правильно".
Это не декларативное описание реакции. Реакция весьма императивная. Претензия моя тут касалась необходимости искать элементы. Опять же у вас там используется два разных API для поиска, два разных апи для событий. И это всего-лишь в рамках HelloWorld. Уверен есть и ещё парочка с поиском по селекторам и делегацией событий в духе jQuery.
Это не мой комент, а оригинальный. Собственно у меня тут претензий нет.
- Вот именно что, грамотный код (а это не про флаксоредаксы) было написать лень. И так кода целая гора получилась. Туториал должен учить как правильно, а не как наговнякать по быстрому. Для сравнения — привет мир на $mol — никаких скидок на то, что это "привет мир" — полноценное приложение, выполненное по всем канонам. И кода на порядок меньше, чем у вас.
Ничего не упустил?
Вы чудеснейшим образом не заметили то, что не используете.
Отсутствие статической типизации. Без неё работа над большими проектами крайне не эффективна.
Отсутствие реактивности. Без неё багоёмкость и избыточность кода слишком высока.
Отсутствие связывания данных. Без него композиция компонент затруднена и требует кучу инфраструктурного кода.
- Отсутствие библиотеки высокоуровневых компонент. Вот это — даже до бутстрапа не дотягивает. Уж за столько человеколет должна была бы уже сформироваться библиотека готовых переиспользуемых решений. А нет, видимо ваши компоненты получаются совсем не переиспользуемыми.
Тот факт, что про холивары про БЭМ не утихают во фронтенде ТАК долго уже о чем-то да говорит. Сколько технологий родилось, поднялось на волне хайпа и было забыто, пока нас ругают за читабельность кода, а потом пишут комментарии в духе habrahabr.ru/company/htmlacademy/blog/337286/#comment_10428564? ;)
Давно слышу про bem, но недавно впервые столкнулся с проектом, где бьют по рукам за :first-child, :last-child и прочие не бэмославные селекторы. Чувство дискомфорта не покидает с этим bem'ом. И даже не потому что надо писать километровые классы. Есть ощущение, что он отправляет нас куда-то в каменный век css 1. Ни тебе иерархий, ни первых/последних элементов, ни атрибутов. Только классы-классы-классы… Может Яндексу от этого и хорошо, но в среднем проекте это просто бессмысленное раздутие html, которое делает его плохо читаемым. При наличие аккуратного убористого css всё выглядит намного понятнее. Особенно если вы не хотите по пять раз на дню менять местами форму логина, блок спецпредложений, корзину заказа и новости.
К Яндексу в целом я отношусь хорошо, но Яндекс продвигает Яндекс. А bem — просто ещё один информационный повод с помощью которого проводится продвижение. Помнится, они когда-то и xslt продвигали. Я его даже учил и на нём писал. И где он сейчас? При том что возможности у него действительно большие, сложность в 99% проектов не оправдана. Надеюсь что и bem'овская эпидемия канет в Лету и мы снова сможем верстать трёхстраничные сайты просто на css 3.
P.S.: Открыл главную страницу Яндекса. Я просто оставлю это здесь:
<div class="i-bem popup dropdown-menu__popup dropdown-menu__popup dropdown-menu__popup_list_yes dropdown__popup dropdown__popup popup_domik_trigger popup_user_yes popup_theme_ffffff popup_autoclosable_yes popup_adaptive_yes popup_animate_yes" data-bem="{"popup":{"directions":{"to":"bottom","axis":"right","offset":{"top":-44,"left":5}},"disableoutside":"yes"}}" role="menu">...</div>
Вы правда хотите видеть такой html в своём проекте?
и вообще если у меня компонент имеет структуру ul > li, то почему я не могу написать
.list-component > li
как-будто может быть что-то другое, или при копировании в любое другое место что-то поменяется
или может мы не будем использовать даже :hover, а будем навешивать
list-component__item list-component__item--hover
Не рекомендуется использовать селекторы на теги и каскад от внутренностей одного блока к другому блоку / его элементам.
Зачем нужен БЭМ