Зачем нужен БЭМ


    Следуете ли вы БЭМу, и насколько он востребован вне Яндекса?

    БЭМ расшифровывается как: блок, элемент, модификатор. Это такой набор абстракций, на который можно разбить интерфейс и разрабатывать всё в одних и тех же терминах. Как говорит сайт 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 лет, каким когда-то стал БЭМ. Что это будет? Посмотрим. Я ставлю на веб-компоненты.


    А пока, если вы опишете интерфейс по БЭМу — код будет организован, предсказуем, расширяем и готов для повторного использования. Не только для вас, но и для других.


    Видеоверсия



    Вопросы можно задавать здесь.

    HTML Academy 65,39
    Интерактивные онлайн-курсы
    Поделиться публикацией

    Вакансии компании HTML Academy

    Комментарии 81
    • +8
      На картинке Вадим смотрит на тебя с поднятыми бровями так, как будто ты не используешь БЭМ.
      • 0
        блок отделяется двумя подчёркиваниями, а модификатор — двумя дефисами.
        .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»?
        • 0
          del
          • 0
            В данном примере названием модификатора выступает «search-form_focused».
            • 0
              БЭМ не разделители и подчеркивания. Разделять можете как Вам нравится. Главное показать отношение элемента к блоку и их модификаторы.
            • 0

              Тут имя модификатора — focused

            • +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>

              И я прозрел

              • 0

                О да, помню как первый раз услышал о БЭМ и писал классы длиной в киллометры)

                • 0
                  Теперь и я прозрел. Спасибо.

                  Удивительно, как такой лаконичный пример никто раньше не привёл
                  • 0
                    Я тоже в свое время прозрел на этот счет
                    Отдельно добавлю, что если все таки нужно положить элемент в элемент, то для этого можно использовать одинарное подчеркивание. В связи с этим и удобно использовать именно — как разделитель для модификатора:
                    note__sidebar_title--size_m
                    • 0

                      Мы для отделения слов внутри имени используем -, а не _

                      • 0
                        Тогда все сольется в один большой элемент
                        front-page__section-promo-subinfo-title
                        против
                        front-page__section-promo_subinfo-title
                        или даже
                        front-page__section-promo_subinfo_title
                        Необходимость возникает когда title в блоке действительно не один, а делить все на микроблоки уже слишком
                        • 0
                          Нет ничего плохого в front-page__section-promo-subinfo-title. Зато тут програмно можно определить, что front-page это блок, а section-promo-subinfo-title — элемент.

                          Но я бы скорее сделал front-page__section и front-page__subinfo-title
                      • +1
                        Элементы в элементы запросто кладутся, но остаются элементами самого блока.

                        <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 меняйте эту конкретную. Получается вполне классическое наследование.
                        • 0

                          Во втором примере как вы поменяете цвет иконки в кнопке в шапке, если "кнопка с иконкой" — отдельный переиспользуемый компонент со своим отдельным шаблоном?

                          • 0
                            Не понял, о чём вы.

                            .header__button .button__icon {
                                background: red;
                            }

                            Так?
                            • 0

                              Именно. Надо ли объяснять чем это плохо?

                              • +1
                                Объясните пожалуйста.
                                .header__button > .button__icon {
                                    background: red;
                                }
                                Так тоже плохо?
                                • 0
                                  1. Увеличивается специфичность селектора, а значит в дальнейшем перебивать его придётся не менее специфичным селектором и дублировать стили для увеличенной специфичности (всякие ховеры, фокусы и тп, например).
                                  2. При использовании пробела воздействует не только на элементы нужного нам блока, но и на элементы вложенных блоков того же типа, что зачастую является вредным и неожиданным влиянием.
                                  3. При использовании угловой скобки слишком жёстко завязывается на структуру вложенного компонента. При изменении структуры стиль будет отваливаться, что весьма не просто отслеживать.
                                  • 0
                                    В теории да. На практике тут всё отлично, за полтора года с БЭМ подобных проблем вроде не встречал.
                                    • 0

                                      Да нет, это проблемы из практики. Собственно для решения этих проблем БЭМ и появился. И он замечательно их решает в случае статических страниц. Для использования же в компонентах, нужно сделать ещё один шаг в том же направлении — нужен механизм "каскада", но на уровне компонент, а не дом-дерева. Пресловутый .header__button — это каскад второго уровня. Но уровней бывает нужно больше и тогда БЭМ откатывается обратно до использования дом-каскада. А всего-то, нужно позволить многоуровневый каскад .header__button__icon, но такие классы руками уже не попишешь — нужна автогенерация. У нас она есть. В случае bemhtml, на сколько я понял, есть только костыль с подсовыванием css-классов через данные.

                                    • 0
                                      1. Не понял эту претензию.
                                      значит в дальнейшем перебивать его придётся не менее специфичным селектором
                                      Что в этом плохого?
                                      и дублировать стили для увеличенной специфичности (всякие ховеры, фокусы и тп, например)
                                      Так вроде бы не дублировать, а переопределять только некоторые свойства CSS?

                                      Типа так:
                                      .header__button .button__icon, .header__button .button__icon:hover {
                                          background: red;
                                      }
                                      .header__button .button__icon:hover
                                      {
                                          font-size: 22;
                                      }
                                      Правда я уже несколько лет не писал CSS так что не уверен. Сейчас как раз освежаю знания.

                                      А что вы предлагаете? Вроде CSS специально так и работает.

                                      2. При использовании пробела воздействует не только на элементы нужного нам блока, но и на элементы вложенных блоков того же типа, что зачастую является вредным и неожиданным влиянием.
                                      Так если один верхний блок отличается от другого верхнего блока, значит можно найти такой селектор который однозначно задает свойства только нужному элементу?

                                      3. Пункт принят.
                                      • 0
                                        1. Речь о такой фигне:

                                        /* иконку делаем полупрозрачной */
                                        .button__icon {
                                            opacity: .8;
                                        }
                                        
                                        /* при наведении делаем её непрозрачной */
                                        .button__icon:hover {
                                            opacity: 1;
                                        }
                                        
                                        /* в шапке хотим ещё более прозрачную кнопку */
                                        .header_button .button__icon {
                                            opacity: .5;
                                        }
                                        
                                        /* чиним сломавшийся на предыдущем шаге ховер-эффект */
                                        .header_button .button__icon:hover {
                                            opacity: 1;
                                        }

                                        1. Они могут быть вложены друг в друга даже. Ну это не про кнопки, конечно, а про списки, например.
                                • 0
                                  Самая главная проблема тут — нарушение инкапсуляции. По-хорошему, блок знает только про свои элементы и обращается к ним, а в этом примере блок header обращается к элементам блока button, что ведёт к очень болезненному рефакторингу, когда изменится внутренняя структура блока button.

                                  ru.bem.info/methodology/css/#Принцип-открытостизакрытости
                                  • 0

                                    Обращение через контейнер-владелец единственно правильная практика.

                        • 0
                          > Если поле нужно растянуть на всю ширину, но только в шапке, то блоку или элементу, который отвечает за это растягивание, даётся модификатор.

                          А мы не так делаем. Мы разделяем «внешние свойства» — все что за 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 пробуем. Есть и плюсы, и минусы.
                          • 0
                            навешивать на чужой блок селекторы своих элементов
                            Не могли бы пояснить что это значит, JavaScript пишет типа block.className += " button_selector"?
                            Или копируется разметка, затем верстальщик ручками вписывает нужные селекторы?
                            Может вы о чем то другом.
                            • 0
                              Суть не в том, как именно class навешивается. Суть в том, что есть договоренность:
                              — компонент (блок) сам себе не задает margin/position/flex/width
                              — компонент верстается так, чтобы если его растянули — он бы нормально выглядел
                              — потребитель компонента сам вешает ему margin/flex/и т.п., чтобы его как нужно вставить

                              Т.е. если у нас кнопочка — она без паддингов и резиновая. Если кнопочку надо вставить и, скажем, растянуть пошире и отодвинуть справа — мы на нее вешаем flex и padding снаружи. Можно классом, можно инлайном — тут не суть. Это, вроде как, не чистый BEM, но нам так сильно проще жить.
                          • 0
                            Это называется «миксины».
                            Примешивание блока к элементу. Таким способом удобно позиционировать блоки внутри других блоков.
                            з.ы. это было мое второе «прозрение», как говорят в комментах выше.
                            • 0
                              Прикольно. Честно не знал, думал это мы додумали. Да и автор в статье вон, как пример, предлагает модификатором блок по ширине растягивать.
                            • 0
                              Если я правильно понимаю, вы говорите про классы типа color-red и т.п.
                              Проблема в том, что они ничего не говорят о состоянии блока / элемента, и по факту аналогичны внутренним стилям.

                              Когда вы пишете:
                              $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
                              Благодаря этому блоки можно легко менять местами, вкладывать друг в друга и не бояться конфликтов или влияния.
                              Это надуманная проблема, в реальной разработке в такой гибкости нет необходимости. Те элементы, у которых позиция в разметке может измениться, должны верстаться с учетом возможных изменений и без всяких БЭМ. Среди остальных элементов в любом случае будут элементы, изменение позиции которых потребует изменения стилей, это уже вопрос дизайна и БЭМ здесь ничего не решает.
                              • 0
                                БЭМ решает проблему того, что в процессе разработки вдруг необходимо на страницу добавить блок, который есть на соседней. А этот блок разработчик мог написать с селектором типа .my-page .my-block и в итоге нужно или на новой странице добавлять класс .my-page, что может дать проблемы или оставлять в стилях только .my-block, но тогда его стили могут уже как-то переопределяться в будущем. Разработка же не заканчивается на этом.
                                Кроме того одним махом решается вопрос поддержки — намного проще изменить внутри блока какой-то элемент зная, что он точно изменится везде, а не нужно перепроверять все страницы, где этот элемент может быть.
                                • 0
                                  Блоки, которые могут использоваться ну других страницах или еще где-то должны верстаться без зависимостей в любом случае. В рамках спецификации можно найти много изящных способов, как сверстать элемент полностью самостоятельным и независимым. БЭМ здесь ничего не решает, только уродует и ограничивает.
                                  • +1
                                    БЭМ задает удобные и простые правила как верстать _все_ компоненты полностью самостоятельными и независимыми из коробки
                                • 0

                                  Это не надуманная проблема если в твоей жизни была фирма с общим фирменным стилем на всех сайтах.

                                • 0
                                  Пока что единственные 2 момента меня останавливают от использования БЭМа:
                                  1. Трудно читаемый код с такими длинными классами (в частности html-код)
                                  2. Нет проектов с более, чем 2-мя верстальщиками
                                  • –1
                                    Там тулинг же надо, руками это писать — мазохизм. В LESS и прочих — дублирование убирается через вложенность правил. А в шаблонах или коде — тоже придумываются хелперы. Скажем, в реакте у нас:
                                    const b = new Bem('my-button'); // один раз вверху модуля

                                    b.div({ element: 'caption', mods: [isRed && 'red'] }); // my-button__caption my-button--red -->

                                    У нас нет выделенных верстаков вообще, разработчики всё делают — и JS, и верстку. И всем удобно. Правда, мы уже теперь на CSS-модули переезжаем — они фичи БЭМ-а перекрывают, но суть та же.
                                    • 0
                                      Трудно читаемый код с такими длинными классами


                                      Парадокс в том, что читать верстку, написанную с соблюдением БЭМ несравнимо удобнее. Стоит только начать, вернуться назад желания уже не возникнет.

                                      Нет проектов с более, чем 2-мя верстальщиками


                                      Это еще одно заблуждение. Даже одному писать код по единым понятным правилам намного проще, чем каждый раз придумывать новое решение, конфликтующее с предыдущим.
                                    • +1
                                      идея бема хорошая, а реализация неудобная, вот почему бы не договориться, что модификаторы должны содержать глаголы is, has, и не использоваться самостоятельно, тогда можно писать типа
                                      .button.is-disabled {}
                                      

                                      <button class="button is-disabled" />
                                      

                                      вместо

                                      .button--disabled {}
                                      

                                      <button class="button button--disabled" />
                                      

                                      • 0

                                        Я с бемом использую модификаторы — полет нормальный

                                        • 0

                                          DOM-элемент может представлять собой несколько БЭМ-сущностей (миксы). Например, это может быть одновременно элементы раскладки (page__elem), ответственный за размещение (position, margin, flex — все дела) и непосредственно какой-то блок (block). В таком случае ваши модификаторы-классы могут создавать коллизии. Но если избавиться от миксов, то можно и так. Правда, в таком случае вы увеличите уровень вложенности, и не исключены сложности с раскладкой, там где важен уровень вложенности.


                                          (P.S. А ещё так нельзя было делать в IE6 — во времена, когда создавался БЭМ.)

                                            • 0
                                              при использовании миксинов это может быть проблемой, но во-первых я не понимаю вообще смысл миксинов, т.к. в итоге блок становится винегретом из классов, понять как будет вести себя блок очень сложно на первый взгляд
                                              <div class="menu__item button button_active">...</div>
                                              

                                              а во-вторых, когда ты пишешь компонентный css с использованием например reactjs, то непонятно что вообще за компонент нам надо писать, нужна кнопка, делаешь компонент кнопки и стили для него, нужна менюшка, делаем компонент меню и описываем стили, но когда нужно написать что-то типа того кода что я выше написал, то тут или новый компонент, или новые модификаторы для меню или еще что-то, но никак не смешивание кнопки с меню

                                              ps: я честно не пишу по бэму, и если бы мне попался уже написанный проект с кодом типа
                                              <div class="menu__item button button_active">...</div>
                                              то мне было бы сложно его поддерживать
                                              • 0
                                                Как вы решаете задачу, чтобы кнопка могла быть использована в совершенно любом контексте, могла иметь опциональное активное состояние и при этом, будучи конкретно в пункте меню, обладала определенными отступами от контейнера? Каждый раз создаете лишние DOM-узлы?
                                                • 0
                                                  Вы задали очень хороший вопрос. Все компоненты не имеют своих отступов, любые отступ должны задаваться из вне, чтобы спозиционировать ту же кнопку на странице используется или сетка, или специальные классы хэлперы, которые добавляют маржен или паддинг.
                                                  Я использую vuejs, и например маржен я могу задать через директиву, директива довольно функциональна, так же имеет модификаторы для медиа запросов
                                                  <menu>
                                                      <v-button v-margin.xs.top="1" v-margin.md.top="2">
                                                          send
                                                      </-v-button>
                                                  </menu>
                                                  


                                                  второй очень важный момент, что каждый компонент имеет свой файл со стилями и при импорте вебпак добавлят к названиям классов хэш, что гарантирует уникальные названия классов, чтобы уж наверняка. В моих шаблонах вы никогда не увидите классы, любые стили должны приналежать компоненту и построение шаблонов строго теми средствами, которые предоставляются в проекте, сетка+хэлперы/директивы
                                              • 0
                                                Я пробовал писать так, такие модификаторы действительно создают путаницу.
                                                Наверное, можно, но это сложнее.
                                              • +1
                                                Зачем в модификаторе повторять всю цепочку названия? Моя верстка в общем следует бэму, только все модификаторы, и только они, начинаются с `-` т.е. вместо
                                                <button class="search__button search__button--primary">
                                                идет просто
                                                <button class="search__button -primary">
                                              • 0
                                                А как вы относитесь к css-modules? Ничего придумывать не надо, простые названия, но никогда не пересекутся в двух компонентах и стек технологий чтобы использовать уже есть.
                                                • 0
                                                  Некоторые неверно понимали идею и появлялись даже элементы элементов.

                                                  Когда элемент сам по себе является блоком с элементами внутри и нужно их стилизовать, то тут как нельзя кстати приходятся "элементы элементов":


                                                  [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.


                                                  Как ни автоматизируй, многое в БЭМе приходится делать руками, и возможны конфликты.

                                                  Мы БЭМ классы генерируем автоматически на основе:


                                                  1. Имени блока (mol_button, например).
                                                  2. Имени блока в контексте другого блока (блок mol_button, например, может иметь имя option в контексте mol_select, что даст селектор mol_select_option).
                                                  3. И так далее по дереву компонент.

                                                  Пример из жизни:


                                                  <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 приложении */
                                                  }
                                                  • 0

                                                    Ну а в "шаблоне", конечно, никакого БЭМ-а:


                                                    <= Title $mol_string
                                                        hint <= title_hint @ \Task title
                                                        value?val <=> title?val \
                                                    • +1

                                                      Это не БЭМ. Ключевое отличие БЭМ от любого другого именования с кучей подчёркиваний — можно в любой момент программно определить что есть блок, что элемент, а что модификатор в любом произвольном идентификаторе.


                                                      https://ru.bem.info/methodology/naming-convention/

                                                      • –1

                                                        Я вам предлагаю сделать шаг вперёд и понять, что элемент — это тоже блок, только в пространстве имён другого блока.

                                                        • +2

                                                          В этом нет смысла. Текущая методология достаточно обрезана бритвой Оккама, чтобы быть способной выразить всё, что нужно.

                                                          • 0

                                                            Можно ещё немного обрезать и увеличить выразительность. Ваш текущий подход без "элементов элементов" работает для целиковых страниц, где вы можете руками приписать любой класс к элементу на любой глубине. Но совершенно не работает для компонент (у каждого из которых свой шаблон), ибо вы не можете так просто добавить произвольный класс элементу в шаблон подкомпонента.

                                                            • 0
                                                              У нас всё сделано из компонент (блоков) у каждого из которых свой шаблон. И мы можем добавить произвольный класс элементу или в шаблон компонента (bemhtml) или на уровне данных (bemjson).
                                                              • 0

                                                                Можно пример?

                                                                • 0
                                                                  Когда нужно что-то изменить в готовом компоненте, создаётся модификатор этого компонента на своём уровне, в котором точечно доопределяются/переопределяются элементы этого компонента, добавляя нужные миксы к другим элементам этого компонента или к другим компонентам.

                                                                  Это позволяет не писать стили вида

                                                                  .my-component__element .foreign-component__element

                                                                  а писать

                                                                  .foreign-component_my-style .foreign-component__element
                                                                  • 0

                                                                    Если вводить модификатор на каждое место использования, то каждый компонент будет представлять из себя помойку из кучи неортогональных модификаторов, каждый из которых используется только в одном месте и больше нигде.


                                                                    В примере же у вас ничего принципиально не поменялось — как был каскад, так и остался. Давайте что-то более конкретное. Допустим у нас есть следующие компоненты:


                                                                    1. Абстрактная иконка (ya-icon) текущего цвета
                                                                    2. Абстрактная строка поиска (ya-search) с кнопкой перехода (ya-search__go ya-icon)
                                                                    3. Шапка портала (ya-header) со строкой поиска (bt-header__search ya-search) и меню выбора сервиса.
                                                                    4. Главная страница биткоин-сервиса (btc-morda) с шапкой (btc-morda__header), телом и подвалом.

                                                                    Задача: перекрасить кнопку перехода в строке поиска в шапке на морде биткоин-сервиса в золотой цвет. Как вы это сделаете?

                                                                    • 0
                                                                      Если у вас помойка модификаторов, используемых только один раз, то скорее всего их вводить не нужно, а нужно что-то другое. Например, другой блок.

                                                                      Про ya-icon не понял. Что за абстрактная иконка? Зачем нужна?
                                                                      • 0

                                                                        У меня-то нет помойки. Я просто напишу в /btc/morda/morda.view.css:


                                                                        [btc_morda_header_search_go] { color : gold }

                                                                        Ок, не абстрактная, а конкретная иконка (ya-icon-search ya-icon). Если вопрос в том, зачем отдельный блок, то затем, чтобы через css можно было ею управлять. Например, чтобы текущий цвет шрифта был основным цветом иконки.

                                                                • 0
                                                                  Виталий Харисов — Пришёл в Яндекс в 2005 году как HTML-верстальщик. За время работы в Яндексе прошёл путь от вёрстки независимыми блоками до БЭМ, от офиса в Симферополе в 25 кв.м. до 1200 кв.м., от команды из одного человека до руководства всей разработкой на Украине.

                                                                  vintage я яндексменов чувствую за километр. Они не будут менять свою точку зрения. Это бесполезная затея

                                                                  • 0

                                                                    Вода камень точит :-) Впрочем, $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() + '!');
                                                                                            });
                                                                                        }
                                                                                    }
                                                                                }
                                                                            }));
                                                                        });
                                                                    • 0
                                                                      Ну даже не знаю… :)
                                                                      Я же правильно понимаю, что приведенный сниппет кода должен ярко иллюстрировать ущербность БЭМа?

                                                                      Она в том, что
                                                                      1. Среди возможностей есть по-настоящему асинхронная во всех смыслах модульная система, позволяющая при необходимости точечно переопределять любые модули (стоит ли говорить, что ее использование опционально и БЭМ не перестанет быть БЭМом без нее?)
                                                                      2. Помимо возможности декларативно описать реакцию на изменение модификаторов, можно воспользоваться и соответствующими событиями, которые обладают всеми возможностями нативных событий.
                                                                      3. При необходимости отменить поведение события по умолчанию, нужно (о мой б-г!) вызвать preventDefault().
                                                                      4. В hello-world примере не стали задвигать телегу про единый источник истины, докручивать флакс с привкусом редакса и прочие свистелки, которые каждый имеющий голову разработчик выбирает для себя сам и которые не имеют прямого отношению к тем вещам, за которые отвечает БЭМ.

                                                                      Все верно? Ничего не упустил? :)
                                                                      • 0
                                                                        1. В JS уже есть стандартные модули — стоит использовать их. Отложенная загрузка легко реализуется через механизмы реактивного программирования, без необходимости превращать код модуля в лапшу из колбэков. Пример с догрузкой ваших Я.Карт по требованию. И, кстати, да, у вас слишком много "опционального" и "вариативного". В итоге, в ваших исходниках сложно разбираться и ещё сложнее понять "как писать правильно".


                                                                        2. Это не декларативное описание реакции. Реакция весьма императивная. Претензия моя тут касалась необходимости искать элементы. Опять же у вас там используется два разных API для поиска, два разных апи для событий. И это всего-лишь в рамках HelloWorld. Уверен есть и ещё парочка с поиском по селекторам и делегацией событий в духе jQuery.


                                                                        3. Это не мой комент, а оригинальный. Собственно у меня тут претензий нет.


                                                                        4. Вот именно что, грамотный код (а это не про флаксоредаксы) было написать лень. И так кода целая гора получилась. Туториал должен учить как правильно, а не как наговнякать по быстрому. Для сравнения — привет мир на $mol — никаких скидок на то, что это "привет мир" — полноценное приложение, выполненное по всем канонам. И кода на порядок меньше, чем у вас.

                                                                        Ничего не упустил?

                                                                        Вы чудеснейшим образом не заметили то, что не используете.


                                                                        • Отсутствие статической типизации. Без неё работа над большими проектами крайне не эффективна.


                                                                        • Отсутствие реактивности. Без неё багоёмкость и избыточность кода слишком высока.


                                                                        • Отсутствие связывания данных. Без него композиция компонент затруднена и требует кучу инфраструктурного кода.


                                                                        • Отсутствие библиотеки высокоуровневых компонент. Вот это — даже до бутстрапа не дотягивает. Уж за столько человеколет должна была бы уже сформироваться библиотека готовых переиспользуемых решений. А нет, видимо ваши компоненты получаются совсем не переиспользуемыми.
                                                                    • 0
                                                                      А в чём вы собрались убеждать создателя БЭМ? ;-)
                                                                      В том что можно «Б» без «Э» и «М»? Ну, можно, наверное. С диким оверхедом на дублирование кода, но можно.
                                                        • 0
                                                          del
                                                          • 0
                                                            с БЭМом явно что-то не так, и это не только читабельность кода, иначе не было бы столько холивара
                                                            • 0
                                                              Утверждение примерно в духе «с Windows явно что-то не так, и это не только необходимость перезагружаться при установке обновлений, иначе не было бы столько холива».

                                                              Тот факт, что про холивары про БЭМ не утихают во фронтенде ТАК долго уже о чем-то да говорит. Сколько технологий родилось, поднялось на волне хайпа и было забыто, пока нас ругают за читабельность кода, а потом пишут комментарии в духе habrahabr.ru/company/htmlacademy/blog/337286/#comment_10428564? ;)
                                                            • 0
                                                              Как по мне, то БЭМ — отличное решение для случаев, в которых стили разрабатываются параллельно приложению и формируются в итоге в один большой стайлшит, с которым приходится потом работать не одному разработчику, но если взглянуть поближе на возможности CSS модулей в контексте компонентной разработки, на пример React, то становится ясно как дважды два — для небольших кусочков стилей, относящихся лишь к конкретному компоненту, реюзабельные вместе с ним, лаконичные и модульные, с которыми не надо работать в составе огромного стайлшита, навороченные БЭМ селекторы становятся совершенно излишни и только всё усложняют. Уникальность селекторов может быть обеспечена автогенерацией небольшой части хэша, добавленной к селектору и предвидя замечания, что это может усложнить отладку, тоже не соглашусь — они ни сколько не мешают работать с версткой. Я не стану говорить за другие подходы, я понимаю какой круг проблем призван решить БЭМ, но знаю точно одно — если Вы пишете на React и юзаете CSS modules, то он Вам не нужен.
                                                              • 0

                                                                Давно слышу про 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="{&quot;popup&quot;:{&quot;directions&quot;:{&quot;to&quot;:&quot;bottom&quot;,&quot;axis&quot;:&quot;right&quot;,&quot;offset&quot;:{&quot;top&quot;:-44,&quot;left&quot;:5}},&quot;disableoutside&quot;:&quot;yes&quot;}}" role="menu">...</div>

                                                                Вы правда хотите видеть такой html в своём проекте?

                                                                • –1
                                                                  два чая этому господину

                                                                  и вообще если у меня компонент имеет структуру ul > li, то почему я не могу написать
                                                                  .list-component > li

                                                                  как-будто может быть что-то другое, или при копировании в любое другое место что-то поменяется

                                                                  или может мы не будем использовать даже :hover, а будем навешивать
                                                                  list-component__item list-component__item--hover
                                                                  • +1
                                                                    БЭМ не запрещает использовать :first-child и прочие псевдоклассы.

                                                                    Не рекомендуется использовать селекторы на теги и каскад от внутренностей одного блока к другому блоку / его элементам.
                                                                    • 0
                                                                      Про псевдоклассы я с ходу не нашёл внятных упоминаний на bem.info А под каскадом вы что имеете в виду? ">" и "+"? Вообще было бы здорово если бы помимо этой тугой документации был нормальный cheatsheet. Список css-слекторов с пометками «можно», «нельзя» и «можно только как в примере». Впрочем, может уже и поздно. Джин вылез из бутылки и теперь у каждого немножечко свой bem.
                                                                      • +1
                                                                        Так хорошо

                                                                        .block_theme_big .block__elem

                                                                        А так плохо

                                                                        .block .another-block__elem
                                                                    • –1
                                                                      Если запрещать псевдоклассы типа :last-of-type, то с таким подходом можно еще и запретить использование :hover, :focus и т.п. =)

                                                                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                    Самое читаемое