Как я проект на БЭМ переводил… и перевел

    Связка HTML и CSS (CSS в большей степени) всегда казалась мне несколько «туманной», хуже всего поддающейся контролю и тестированию. Я придумывал для себя различные правила и пытался так или иначе стандартизировать свой подход, но не было ощущения, что «вот, это оно». Я несколько раз мельком знакомился с БЭМ (и не только), читал статьи на эту тему, но дальше чтения дело не заходило. Но чем дальше, тем сильнее было ощущение необходимости в наличии определенной строгой методологии. В конце концов, я решил попробовать внедрить БЭМ на одном из своих проектов, где без этого, на мой взгляд, было не обойтись. Речь идет о CMS, упрощенную страничку бекенда которой я приведу в качестве примера верстки:



    Сразу хочу заметить, что БЭМ — это далеко не методология на все случаи жизни, и вопрос о необходимости ее применения в том или ином проекте следует рассматривать в частном порядке (в том числе исходя из того, нравится она вам или нет). Также, в силу того, что я не использовал предлагаемую специфическую файловую структуру или генерацию HTML, о них говорить не будем (позднее я все-таки разделил CSS-файл на отдельные части, соответствующие блокам, но этим решил пока ограничиться). Также, уже достаточно много (например, вот и вот) написано о достоинствах и недостатках этого подхода в целом, поэтому говорить об этом тоже не будем, я просто поделюсь своим опытом и размышлениями на эту тему, предполагая, что с сутью вы уже знакомы.

    Приступаем


    Итак, в моем случае CMS — большой и модульный проект с открытым исходным кодом, в котором каждый модуль может иметь собственные куски бекенда, используя общие и добавляя к ним специфические элементы интерфейса. В идеале, модули должны разрабатываться и другими разработчиками. Думаю, это как раз такой проект, где применение БЭМ (или другой серьезной методологии) является не просто уместным, а необходимым. Особенно это важно, т. к. многие из веб-разработчиков считают CSS побочной, недостойной внимания и глубокого изучения технологией, пытаясь добиться необходимого результата путем копирования готовых кусков разметки и стилей, концентрируясь на том, что им больше нравится, получая в итоге неподдерживаемые таблицы стилей ужасающего качества.

    На первых порах проще всего было вообще не трогать исходный код моего проекта, т. к. он достаточно крупный. Вместо этого я создал простой HTML-документ (/index.html) и таблицу стилей (/css/style.css) для него, положил все это на рабочий стол для удобства и решил для начала сверстать несколько фрагментов с картинки выше, используя для этого Notepad++ и браузер. (В результате я хотел получить страницу, содержащую вообще все необходимые мне составные части, и уже затем, в случае успеха, перенести это в свой проект. Упрощенный результат доступен для изучения по ссылке в конце статьи; ссылку на то, как все вышло в реальности, тоже можно глянуть там.)

    Кнопки


    Я решил начать не со структуры, а с маленького блока кнопки — button. Кнопки у меня бывают 3-х типов: позитивное действие, негативное действие и нейтральное действие. Отличаются они лишь цветом, поэтому эти отличия я описал в виде булевых модификаторов, соответственно, button--positive, button--negative и button--neutral (я выбрал альтернативный синтаксис для модификаторов и вместо одного символа подчеркивания использую два дефиса — для меня это выглядит значительно нагляднее).

    В результате в HTML кнопка описывается таким образом:

    <button class="button button--positive" type="button">Text</button>
    

    Также допусти́м и такой вариант (одной из особенностей БЭМ является, в идеале, независимость внешнего вида от используемого тега, хотя я считаю достаточным исходить из того, к каким тегам класс может применяться, и не пытаться предусмотреть все, раздувая таблицу стилей лишними правилами):

    <a class="button button--neutral" href="#">Cancel</a>
    

    Выглядит вполне читаемо и понятно. Посмотрим теперь на CSS:

    .button {
      border: none;
      cursor: pointer;
      font: normal 15px 'PT Sans', sans-serif;
      line-height: 20px;
      display: inline-block;
      padding: 5px 10px;
    }
    

    Кнопка описана очень просто. Хотя я и встречал рекомендации сбрасывать значения всех правил внутри своих классов (чтобы на них не влияло окружение), но, как по мне, это уже слишком, и требуется лишь тогда, когда есть реальный шанс, что вы будете повторно использовать ваш блок в каком-то другом проекте, где стиль верстки отличается от вашего (например, вы разрабатываете какой-то виджет, который нельзя вставить как iframe в целевую страницу). Класс блока button, как и требует БЭМ, никаким образом не специфицирует свои размеры или внешние отступы.

    Идем далее:

    .button--positive {
      background-color: #87b919;
      color: #fff;
    }
    
    .button--positive:hover {
      background-color: #a0d71e;
      color: #fff;
    }
    
    .button--negative {
      background-color: #ff4100;
      color: #fff;
    }
    
    .button--negative:hover {
      background-color: #ff7346;
      color: #fff;
    }
    
    .button--neutral {
      background-color: #f0f0f0;
    }
    
    .button--neutral:hover {
      background-color: #f5f5f5;
    }
    

    Эти классы определяют модификаторы для различных типов кнопок (в зависимости от действия) и их состояния при наведении на них курсора мыши.

    Посмотрим на наши кнопки вживую:


    По-моему, хорошо.

    Группы кнопок


    В моем проекте я практически нигде не использую кнопки сами по себе, они почти всегда сгруппированы в группы (например, «Сохранить» и «Отмена» в форме). В каждой группе кнопки должны быть расположены горизонтально, на расстоянии ровно в 1 пиксель друг от друга. Чтобы не испытывать затруднений с выдерживанием этого расстояния (в случае с inline- или inline-block-элементами оно зависело бы от форматирования HTML, а именно, от наличия пробела между тегами), проще всего добавить кнопкам правило float: left, но только тогда, когда кнопка является элементом группы кнопок (т. е. само собой было бы неверно добавлять это правило непосредственно блоку button).

    Итак, опишем блок группы кнопок buttons с единственным элементом buttons__button, представляющим кнопку, входящую в группу. Тогда HTML группы кнопок будет выглядеть вот так:

    <div class="buttons">
      <button class="buttons__button button button--positive" type="button">Send</button>
      <a class="buttons__button button button--neutral" href="#">Cancel</a>
    </div>
    

    Рассмотрим CSS:

    .buttons {
    }
    

    Класс блока buttons пуст.

    .buttons::after {
      content: '';
      display: block;
      clear: both;
    }
    

    Поскольку к кнопкам внутри группы будет применяться правило float: left (причину я описал выше), я отменяю обтекание таким образом. Кстати, этот способ закрытия потока обтекания float нравится мне больше всего, хотя в устаревших браузерах он и не будет работать (чаще всего ориентироваться на них нет необходимости). В любом случае, не в этом суть.

    .buttons__button {
      float: left;
      margin-right: 1px;
    }
    

    Здесь мы непосредственно описываем элемент-кнопку, входящую в группу, с отступом в одну точку справа.

    .buttons__button:last-child {
      margin: 0;
    }
    

    Последняя кнопка в группе не должна иметь отступа справа, используем псевдокласс :last-child для этого. Я использовал именно псевдокласс, а не модификатор, т. к. все без исключения кнопки в группах, если они расположены последними, не должны иметь этот отступ справа. Считаю использование модификаторов в таком случае излишним.

    На мой взгляд, получается достаточно здорово. Сами по себе блоки никак не позиционируют себя, не описывают внешних отступов. Но когда мы помещаем блок в другой блок, он как бы одновременно становится элементом своего блока и именно класс элемента позволяет дополнительно специфицировать все необходимые правила его расположения, если они необходимы. Кстати, я всегда располагаю классы элемента первыми, затем следуют классы модификаторов элемента, а уже затем — классы блока и его модификаторов. Это очень упрощает чтение HTML, т. к. если классов много, то сразу понятнее, что во что входит. Еще момент (на всякий случай). Порядок применения CSS-классов определяется порядком их следования в CSS-файле (а не в атрибуте class, как могло бы показаться), поэтому объявлять классы следует начинать с самых простых блоков, и в самом конце размещать блоки, отвечающие за общую структуру страницы.

    Вот как наша группа кнопок выглядит в браузере:


    На этом с кнопками мы почти покончили, идем дальше.

    Текстовые поля и текстовые области


    Далее я решил разобраться с другими элементами управления. Аналогичным образом описал блоки текстового поля text-box и текстовой области text-area (текстовую область рассматривать не будем, т. к. блоки практически идентичны — в исходниках примера можно посмотреть). Далее приведен HTML блока текстового поля. Дополнительно добавлен модификатор text-box--required, означающий, что поле является обязательным к заполнению (он добавляет красную полоску справа от поля):

    <input class="text-box text-box--required" type="text" />
    

    Соответствующие CSS-классы выглядят так:

    .text-box {
      background-color: #f0f0f0;
      border: none;
      font: normal 15px 'PT Sans', sans-serif;
      line-height: 20px;
      outline: none;
      padding: 5px 10px;
      resize: none;
    }
    
    .text-box:hover {
      background-color: #f5f5f5;
    }
    
    .text-box:focus {
      background-color: #f5f5f5;
    }
    
    .text-box--required {
      border-right: 5px solid #ff4100;
    }
    

    Ничего особенного здесь нет, за исключением, повторюсь, последнего модификатора text-box--required. У текстовой области тоже есть такой, но называется он text-area--required.

    Выглядит наше текстовое поле следующим образом:


    Поля форм


    Как и в случае с кнопками, текстовые поля и области редко применяются сами по себе в моем проекте. Чаще всего они используются в составе форм в виде полей форм (совокупность заголовка и текстового поля, например). Т. е. формы собираются из небольших готовых кусков, а не из отдельных элементов управления. Поэтому я решил добавить блок field, и описать, как ведут себя заголовки и текстовые поля и области внутри поля формы с помощью элементов field__label, field__text-box и field__text-area. В итоге HTML поля формы с текстовой областью выглядит так:

    <div class="field">
      <label class="field__label label">Body</label>
      <textarea class="field__text-area text-area"></textarea>
    </div>
    

    Все просто. Еще раз обратите внимание на порядок следования классов. Сперва, например, следует field__label, а label — после него, т. к. тег label является в первую очередь элементом field__label своего блока field, а уже потом независимым блоком label. Такое единообразие очень помогает. Рассмотрим CSS:

    .field {
    }
    

    Этот класс пуст. При отображении полей форм непосредственно в формах нам потребуется, чтобы между ними были вертикальные отступы, но мы опишем это в соответствующем элементе form__field блока form далее.

    .field__label {
      display: block;
      margin-bottom: 1px;
    }
    

    Заголовки внутри блока field будут выводиться с новой строки и иметь отступ в один пиксель снизу.

    .field__text-box {
      width: 430px;
    }
    
    .field__text-area {
      width: 430px;
      height: 190px;
    }
    

    Этими двумя классами мы задаем размеры для текстовых поля и области, когда они являются элементами поля формы. Результат всего этого следующий:


    Также часть полей форм у меня являются локализируемыми (мультиязычными). Им необходим дополнительный визуальный маркер для указания языка, к которому относятся входящие в них текстовые поля или области. В HTML поле формы с набор локализируемых текстовых полей выглядит следующим образом:

    <div class="field">
      <label class="field__label label">Subject</label>
      <div class="field__culture">
        <div class="field__culture-flag">en</div>
      </div>
      <input class="field__text-box field__text-box--multilingual text-box text-box--required" type="text" />
      <div class="field__multilingual-separator"></div>
      <div class="field__culture">
        <div class="field__culture-flag">ru</div>
      </div>
      <input class="field__text-box field__text-box--multilingual text-box text-box--required" type="text" />
    </div>
    

    Обратите внимание на набор классов текстового поля, их четыре. Давайте еще раз по ним пройдемся. Класс field__text-box определяет размеры текстового поля внутри поля формы, field__text-box--multilingual добавляет небольшой дополнительный отступ справа, чтобы символы при наборе не залезали под маркер языка, который отображается поверх текстового поля. Класс text-box определяет основные параметры текстового поля, а text-box--required добавляет красную полоску справа от поля.

    Новые CSS-классы:

    .field__culture {
      position: relative;
      left: 450px;
      width: 0;
      z-index: 10;
    }
    
    .field__culture-flag {
      background-color: #323232;
      color: #fff;
      cursor: default;
      font-size: 8px;
      line-height: 16px;
      text-align: center;
      text-transform: uppercase;
      position: absolute;
      left: -23px;
      top: 7px;
      width: 16px;
      height: 16px;
    }
    
    .field__multilingual-separator {
      height: 1px;
    }
    

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

    Формы


    Теперь, рассмотрим блок формы form. Формы состоят из полей форм и групп кнопок, которые у нас уже описаны, но добавляют к ним вертикальные отступы с помощью классов элементов form__field и form__buttons. Вот так выглядит упрощенный HTML блока form:

    <form class="form">
      <div class="form__field field">
        <label class="field__label label">Body</label>
        <textarea class="field__text-area text-area"></textarea>
      </div>
      <div class="form__buttons buttons">
        <button class="buttons__button button button--positive" type="button">Send</button>
        <a class="buttons__button button button--neutral" href="#">Cancel</a>
      </div>
    </form>
    

    А вот так выглядит его CSS:

    .form {
    }
    
    .form__field {
      margin-top: 10px;
    }
    
    .form__buttons {
      margin-top: 20px;
    }
    

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

    В браузере форма целиком выглядит так:


    Таблицы


    Теперь займемся немного более сложным элементом — таблицей. Думаю, все знают, что таблицы следует верстать таблицами (т. к. это семантически верно и имеет хорошую поддержку браузеров), но, в случае с адаптивной версткой, иногда удобнее все-таки это делать, используя теги общего назначения div со стилями, вроде display: table. В таком случае, на мобильных устройствах горизонтальную таблицу легко превратить в вертикальный список, всячески манипулируя отображаемыми данными (что-то можно скрыть, а что-то — объединить). Как бы там ни было, для реализации таблиц в своем проекте я решил использовать table, но, отчасти в качестве эксперимента, перед этим сверстал ее с использованием div. Прелесть независимости БЭМ от тегов в том, что, заменив затем теги div на table, tr и td, мне ничего не пришлось изменять в своем CSS-файле, таблица выглядела идентично. Я привел оба варианта для сравнения.

    Стандартная таблица в HTML выглядит так:

    <table class="table">
      <tr class="table__row">
        <th class="table__cell table__cell--header">Cell</th>
        <th class="table__cell table__cell--header">Cell</th>
        <th class="table__cell table__cell--header">Cell</th>
      </tr>
      <tr class="table__row">
        <td class="table__cell">Cell</td>
        <td class="table__cell">Cell</td>
        <td class="table__cell">Cell</td>
      </tr>
    </table>
    

    Как видим, каждому тегу дан класс. Может показаться непривычным, зато это дает возможным безболезненно поменять table, tr и td на div и не визуально не заметить различия.

    CSS таблицы:

    .table {
      border-collapse: collapse;
      display: table;
      width: 100%;
    }
    
    .table__row {
      display: table-row;
    }
    
    .table__cell {
      font-weight: normal;
      text-align: left;
      vertical-align: top;
      display: table-cell;
      padding: 5px 10px;
    }
    
    .table__row:hover .table__cell {
      background: #ffff96;
    }
    
    .table__cell--header {
      background: #f0f0f0;
    }
    
    .table__row:hover .table__cell--header {
      background: #f0f0f0;
    }
    

    Как видим, для самого блока, как и для его элементов, установлены правила display: table, display: table-row и display: table-cell. Благодаря этому блок становится относительно независимым от тегов. По сути, повторюсь, не думаю, что есть смысл в этих правилах, если вы уверены, что таблица будет всегда сверстана именно стандартными табличными тегами.

    Ну и наконец, посмотрим на результат вживую:


    Меню


    Переходим к завершающему этапу. Меню представлены блоком menu. Каждое меню может содержать несколько групп элементов меню (элемент menu__group), каждая из которых, в свою очередь, может содержать один заголовок группы элементов меню (элемент menu__group-title) и несколько элементов меню (элемент menu__item). Вот соответствующий HTML:

    <div class="menu">
      <div class="menu__group">
        <div class="menu__group-title sub-title">
          Group title 1
        </div>
        <a class="menu__item" href="#">Menu item 1</a>
        <a class="menu__item" href="#">Menu item 2</a>
        <a class="menu__item" href="#">Menu item 3</a>
      </div>
    </div>
    

    Я думал над тем, чтобы сделать элементы menu__group и menu__item отдельными блоками, но не нашел аргументов в пользу такого решения: нигде больше они не используются, это привело бы лишь к увеличению количества классов.

    Вроде бы все очевидно, но для наглядности приведу еще и CSS:

    .menu {
    }
    
    .menu__group {
    }
    
    .menu__group-title{
    }
    
    .menu__item {
      display: block;
      padding: 5px 0;
    }
    

    В данном случае у меня пусты некоторые классы. Как видим, например, внешний вид заголовков групп элементов меню определяется общим блоком sub-title (я на нем не останавливался — посмотрите, пожалуйста, в исходниках). Необходимости в пустых классах нет (скорее, есть необходимость в их удалении). Я их решил оставить для наглядности нашего примера.

    Меню само по себе выглядит так:


    Общая структура


    Напоследок, рассмотрим общую структуру страницы. Но перед этим я хотел бы коснуться еще одного момента. Дело в том, что в основном в статьях по БЭМ рекомендуют не иметь в CSS-файле правил, не относящихся к блокам. Т. е. общих правил, применимых ко всему документу (например, с селектором по тегу, а не по классу). Я решил не идти этим путем, т. к. в таком случае увеличивается количество правил, которые необходимо дублировать в каждом блоке или элементе. Я не вижу особой причины делать это в моем случае. Если задуматься, то все блоки в моем проекте описываются в рамках единого контекста, и он неизменяем, поэтому вполне допустимо, например, задать общий стиль для текста, и во всех блоках отталкиваться от него, т. к. они все-равно должны иметь обобщенный стиль.

    Кроме того, мне кажется лишним назначать класс для каждого заголовка, абзаца в тексте, каждой ссылки. В таком случае, при использовании, например, WYSIWYG-редактора нам потребовалось бы добавлять эти классы вручную (или делать это автоматически при сохранении). Так или иначе, это лишнее неудобство.

    Вернемся к общей структуре. Я решил представить ее одним блоком master-detail с двумя основными элементами: master-detail__master и master-detail__detail, отвечающими, соответственно, за левую-темную и правую-светлую части страницы.

    В master-detail__master я добавил два меню. Одно меню не содержит никаких дополнительных классов элемента master-detail__master, т. к. нет нужды дополнять его какими-то CSS-правилами. Второе же меню является одновременно элементом master-detail__secondary-menu, что позиционирует его внизу элемента master-detail__master. Дополнительно, элементы этого второго меню «замиксованы» с элементом master-detail__secondary-menu-item, что придает им серый цвет.

    Не буду приводить HTML/CSS этого блока, т. к. он слишком громоздкий, и его необходимо рассматривать в контексте остального содержимого страницы. Поэтому, предлагаю взглянуть на исходники тестового примера, ссылку на которые можно найти ниже.

    Также на странице остался еще один блок — табы. Решил, что описывать их уже не имеет смысла, т. к. блок очень прост.

    Ну что же, в итоге мы получим такой результат, как на первой картинке.

    Выводы


    Зачем я решил написать это? Когда я принялся разбираться с БЭМ у меня было много вопросов, на которые я не находил однозначных ответов. Это был некий полуфабрикат идеи. Было интересно перестроиться и взглянуть по-новому на процесс HTML-верстки, отказаться от использования каскадов и так далее. В результате я так или иначе нашел для себя решения, и мне захотелось поделиться этим опытом, чтобы постараться упростить для кого-то этот процесс «привыкания и перестроения», показав еще одну точку зрения.

    Методология в целом мне понравилась. Самое важное, на мой взгляд, что она загоняет разработчика в достаточно жесткие рамки в плане структурирования, стиля именования и так далее. В итоге в большинстве случаев есть всего один ответ на вопрос «как?», что очень здорово (особенно, в крупных и командных проектах).

    Стану ли я применять БЭМ на мелких и простых проектах? Пока не знаю. Хотя я и не испытывал вообще никаких лишних сложностей и не заметил лишнего «напряга» из-за увеличившегося количества классов, но все-таки следование методологии требует несколько бо́льших усилий, чем при «традиционном» подходе. Хотя, вполне возможно, это из-за недостатка опыта и сноровки.

    Надеюсь, было интересно. Вживую посмотреть и потрогать можно тут, а здесь лежит реальная демка проекта, кусок упрощенной админки которого я привел в качестве примера. Там можно глянуть в реальном масштабе как это выглядит, а также, при желании, сравнить с тем, что было раньше.
    Поделиться публикацией
    Комментарии 52
      +3
      Как я проект на БЭМ переводил… и перевел

      1) Взял проект.
      2) Перевёл на БЭМ.

      the end.
        –4
        Почему для большого и сложного модульного проекта Вы выбрали устаревший и кривой костыль в виде БЭМ'а, а не компонентный подход, который сам собой напрашивается для решения подобных задач?
        • НЛО прилетело и опубликовало эту надпись здесь
            +3
            Если вы про Web Components, то несколько лет назад я читал о том, что за ними будущее. Сейчас за ними, возможно, все так же будущее, но не настоящее.

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

            На самом деле, сколько статей я не читал про БЭМ, везде одни и те же комментарии и ответы на них. Думаю, чтобы понять преимущество (не совершенство, а преимущество со всеми его недостатками) таких вещей, как БЭМ, над их отсутствием, необходимо сперва в полной мере ощутить недостатки «традиционного подхода».
              –2
              Уже давно настоящее, а не будущее. Взять тот же Polymer Project — поддержка всеми браузерами в т.ч. IE 11+. React, Angular 2 — так же компонентный подход. И все они позволяют инкапсулировать стили.
                +2
                Мне нужна максимально простая реализация, в которой максимально просто разобраться, не требующая ничего стороннего. Как минимум потому, что моя цель — чтобы над проектом работали и другие люди.

                Полимер когда-то смотрел — не понравился. Сейчас еще раз открыл, глянул верстку их семпла-магазина:



                Это читабельнее моей верстки? По-моему их страница намного проще, а верстка значительно сложнее.
                  +2
                  Вы смотрите не верстку, у уже отработавший код и т.к. Ваш браузер (ie/edge судя по ужасному шрифту) полноценно не поддерживает web components, используется полифил. Естественно при верстке всего этого нет и да, она гораздо читабельнее чем БЭМ.



                  Хотите сказать, что «menu__group-title sub-title» удобнее и лучше читается, чем пример выше?
                    +2
                    Повторюсь, мне не нравится необходимость использовать большую стороннюю либу для этой цели, поэтому я даже не стану рассматривать такой вариант. Ее мало кто знает и используют, а голый CSS знают почти все. Когда web components станут стандартом, вполне возможно, я напишу новую статью.
                      0
                      Попробуйте на Object Oriented CSS. Он с точки зрения понятности, рациональности и оптимизации гораздо лучше и читабельнее чем БЕМ.
                        +2
                        Я отдал предпочтение именно БЭМ, т. к. многие крупные проекты используют его (чаще, конечно, собственные имплементации этой концепции). Но за ссылку спасибо.
              +2
              Есть подозрение, что под словом «БЭМ» вы понимаете что-то свое, если противопоставляете компонентному подходу.

              На bem.info в «Основных понятиях» сказано буквально следующее: «Блок — логически и функционально независимый компонент страницы, аналог компонента в Web Components. Блок инкапсулирует в себе поведение (JavaScript), шаблоны, стили (CSS) и другие технологии реализации.»
              +4
              <button class="buttons__button button button--positive" type="button">Send</button>
              

              — настоящая победа синтаксиса БЭМ над здравым смыслом и чувством меры.
                +1
                Дело вкуса. Мне приведенный отрезок кажется изящным (возможно, потому что я его написал и я знаю, почему это должно быть именно так). Проведя немного времени изучая проект, человек со стороны начнет легко разбираться в HTML/CSS составляющей и результаты экспериментов с гораздо большей вероятностью будут предсказуемыми.

                У меня была возможность сравнить (и, при желании, она есть и у вас) оба подхода, т. к. я решил переходить на БЭМ имея уже крупный и рабочий проект. В процессе я получил удовольствие, избавился от дублирования, получил совершенно формализированную и строго структурированную верстку и стили. У меня больше нет ощущения, что это вышло из под моего контроля. Но мне интересно, какую альтернативу вы предлагаете?
                  +4
                  А так?

                  <button class="buttons__button button button--positive" type="button" data-button="button">Button</button>
                  
                  +1
                  А что вы понимаете под «здравым смыслом и чувством меры»? А то у каждого они свои.

                  Что касается именования, то оно тут не шибко удачное. Вместо buttons__button лучше бы подошло что-то вроде actions_item. Модификатор стоило бы назвать type: button_type_positive (в классической нотации модификаторы обособляются одним символом подчёркивания). И так во многих местах. В качестве примера реализации можно посмотреть на библиотеку bem-components.

                  Если вы про тег <button>, то стоит напомнить, что блок button не обязательно кнопка, это может быть и ссылка, выглядящая как кнопка. (В примере она идёт как раз за процитированной вами строчкой.) Кроме того, использование классов даёт всю ту же модульность: можно параллельно вводить БЭМ и использовать старую вёрстку (если не сильно мешают каскадные селекторы, но их можно обособить), спасибо автономности блоков.
                    0
                    В моем случае блок «кнопка» подразумевает не тег «кнопка», а именно визуальный компонент «кнопка», который может быть технически и ссылкой (у меня в примере такая есть), но выглядеть должен именно как «кнопка». Отсюда такое название. Вариант actions мне совершенно не нравится, т. к. оно не отражает суть блока и может подразумевать все что угодно. Посудите сами, если вы видите ссылку с таким классом, какая ассоциация у вас возникнет? Уверен, что весьма обобщенная. Другое дело, когда блок называется «кнопка».

                    Что касается синтаксиса для модификаторов, то я указал, что выбрал один из альтернативных вариантов, который, кстати, приведен на сайте bem.info. Мне он понравился больше. Там же описаны и булевые модификаторы, которые компактнее стандартных и не менее (даже более, как по мне) читабельные.
                      +1
                      actions — это не ссылка или кнопка, это блок с набором активных компонентов, например тулбар, у вас — ряд кнопок. actions_item — элемент блока, активная единица (миксуется к button, задаёт расположение).

                      Булевы модификаторы бывают, но у вас два модификатора про одно и тоже. В итоге выходит три варианта, включая отсутствие модификатора. Это уже совсем не про булев модификатор.
                        +1
                        Я понял, что вы имеете в виду насчет модификаторов. Возможно действительно стоило пояснить, что они все имеют общий смысл, таким образом. Спасибо!
                  –2
                  Зачем вам бэм, когда есть модули?
                    0
                    Писал выше, что не хочу ничего слишком специфического и стороннего использовать, чтобы не повышать порог вхождения в проект. Если честно, мне и не нравится ничего, связанного с HTML/CSS, кроме голого HTML и CSS. Но вот БЭМ пришлась по душе.
                    +4
                    Мне кажется, со временем, человек, который занимается версткой, приходит к чему-то похожему на bem, лично у меня так и вышло.
                      0

                      Мне жутко не нравится БЭМовские селекторы, поэтому я использую смесь из camelCase и kebab-case вида 'блок-элемент-модификатор'.


                      .tableView-cell {}
                      .tableView-cell-selected {}
                      .userList-item {}
                      .userList-item-deleted {}

                      А так как CSS регистрочувствителен, то никаких проблем с этим нет. Так же иногда я прибегаю к модификаторам с префиксом в виде дефиса.


                      .tableView-row.-selected {}

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

                        0
                        Я так понимаю, вы используете +- то же самое, но с немного другим синтаксисом. Думаю, БЭМ можно как раз и рассматривать как «референсную» методологию, наталкивающую на правильные мысли. Главное не в конкретной реализации и даже не в конкретной методологии, а в ее наличии и грамотной имплементации.
                          0

                          Еще забыл упомянуть двубуквенный префикс для переиспользуемых css-библиотек: my-tableView-row.

                          +2
                          Вы удивитесь, но среди предлагаемых на официальном сайте вариантов нейминга в том числе присутствует camelCase.
                            0

                            Вы правы – удивлюсь ) Раньше этого не было.

                          +1

                          Чистое ИМХО, но класический БЕМ выглядит перегруженным, и при написании, хоть и есть логика в именовании компонентов, но доставляет больше неудобств, чем ощущение 100% полезности. Склоняюсь к использованию более наглядных методов, которые ближе к CSS:


                          • Разбивка компонентов на файлы BlockName.scss
                          • Использование в этом файле префикса .BlockName для всех классов (scope)
                          • Использование .BlockName.is-modifier в качестве модификатора
                          • Все дочерние элементы .BlockName-element.is-modifier

                          Пример: .TopNav-item.is-active выглядит по-моему лучше, нежели .top-nav__item .top-nav--is-active.
                          Но да, нужно признать, это все вариации на тему БЭМ.

                            –1
                            Видите, а я наоборот нахожу синтаксис наглядным и удобным. Что поделать — страдаю перфекционизмом, строгая формализация, последовательность и единообразие для меня важнее наличия большего количества классов и немного странного синтаксиса. Хотя ваш подход в своей сути это то же самое.
                              –1
                              Поддерживаю. Сам примерно так и пишу. Но к элементам внутри компонента обращаюсь через .BlockName > тег {....} или .BlockName:nth-child(){....}, не засоряя разметку классами. Т.е. класс имеет только компонент, а не все абсолютно все теги в документе. Этот корневой класс компонента создаёт как бы область видимости для всех вложенных элементов.

                              По поводу модификаторов, на каком-то старом докладе про бэм задали вопрос докладчику: «зачем использавать .top-nav__item .top-nav--is-active, если можно .top-nav__item.is-active?» Так смешно было, докладчик ушёл от ответа мотивировав тем что не понял вопроса, потому как не знал разницу между .class1 .class2 и .class1.class2 )))
                                +1
                                Боюсь, в таком случае вы будете вынуждены изменять CSS при каждом изменении HTML и наоборот. Такая жесткая привязка к тегам и уровню вложенности превращает верстку в монолит, который ломается при любом изменении. Также, например, если вы сделали крупный блок, а затем в другом месте вам понадобился его кусок, то вам придется его переверстывать, т. к. просто скопировать и вставить его туда не выйдет, т. к. он полностью привязан к своему контексту.
                                  0
                                  Вот именно в монолит, только не вёрстка превращается в монолит, а компонент, который можно переносить только целиком. Другое дело, что именно обзывать компонентом/блоком; понятное дело что не кусок разметки на несколько экранов.
                                  0

                                  .BlockName > тег — во-первых как написал DmitrySikorsky это не-гибко, во-вторых производительность этих селекторов ниже. В-третьих, если применять SCSS — это превращается во что то структурированное:


                                  // TopNav.scss
                                  
                                  .TopNav {
                                    &-item {
                                      &.is-active { }
                                    }
                                    &-element { }
                                  }
                                    +1
                                    Раз уж мы за scss и отходим от
                                    не хочу ничего слишком специфического и стороннего использовать
                                    то почему бы не вот так:
                                    // .TopNav.scss
                                    .container {
                                    }
                                    .item {
                                     &_active {
                                       composes: item; //можно и без этого, тогда на элемент нужно вешать оба класснейма
                                     }
                                    }
                                    


                                    //TopNav.jsx - не обязательно React, можно заимпортить где угодно
                                    import theme from './TopNav.scss';
                                    
                                    export const TopNav = props => ({
                                     <nav className={css.container}>
                                      <a className={css.item}></a>
                                      <a className={css.item_active}></a>
                                     </nav>
                                    });
                                    


                                    Вы получаете инкапсуляцию стилей, автогенерацию класснеймов и отсутствие коллизий.
                                    В зависимости от настроек бандлера, класснеймы могут быть вот такие: .TopNav__container, .TopNav__item, .TopNav__item_active. Дополнительно можно добавить небольшой хэш (5-6) символов в конец.
                                      0
                                      Прошу прощения за опечетку,
                                      import css from './TopNav.scss';
                                      
                                      0
                                      как раз селектор > и даёт мне уверенность, что всё будет без проблем в будущем при переносе компонента в другое место. Что касается производительности селекторов — то это какой-то боян или миф, сколько можно про это говорить на каждом шагу, хоть кто-то делал реальные замеры (на реальных проектах, а не на синтетике в 1000000 тегов), и возможно ли это впринципе?

                                      По поводу гибкости, как я написал выше, компонент монолитен, переноси куда хочешь. Хочешь взять часть из компонента — будь добр сверстай по новому, поэтому компоненты делать надо небольшими.

                                      И потом в будущем при переносе компонента в другой проект, даже если имя корневого класса совпадёт с каким-то уже существующим классом в проекте — достаточно будет только изменить один раз это имя в разметке и стилях, и всё станет на свои места, и внутрянка компонента не поломается благодаря селектору >.

                                      Зато какая чистота в разметке — любо дорого посмотреть))
                                        +1

                                        То есть вместо:


                                        <div class="NavBar">
                                          <button class="NavBar-item is-active">...</button>
                                        </div>

                                        Вы используете:


                                        <div class="NavBar">
                                          <button>...</button>
                                        </div>

                                        И стили:


                                        .NavBar {
                                          > button {
                                            &.is-active { }
                                          }
                                        }

                                        ?


                                        Тогда если вдруг нужно использовать вместо <button /> например <a />, нужно не только менять разметку, но и лезть править стили?

                                          +1
                                          А вот еще пример:
                                          <div class="NavBar">
                                           <button class="NavBar-item"></button>
                                           <div id="а_мало_ли" class="NavBar-sub">
                                            <button class="NavBar-item"></button>
                                           </div>
                                          </div>
                                          


                                          Как быть? > уже не спасет
                                            0
                                            Думаю, подразумевается 2 >
                                            .NavBar > div > button
                                            


                                            Но тогда что делать, если у нас несколько кнопок на одном уровне вложенности, а внешнее оформление у них разное? Придется все-равно использовать класс, либо как-то псевдоклассом по индексу идентифицировать. Это очень жестко.
                                              0
                                              Ну то есть стили гвоздями прибиваются к верстке. Да уж, удобно…
                                            0
                                            Если вместо кнопки поменять на ссылку, то это уже изменение разметки блока, т.е. если ты лезешь в блок/компонент — то буть добр и стили подкорректируй, потому что по сути создаётся новый компонент. А моя идея компонента, чтоб можно было переносить его куда угодно, а не изменять. Ведь суть какая — сверстать один раз и пользоваться в будущем. Почему это вдруг менять кнопку на ссылку — это можно, это нормально, а вот уже в стили лезть чтоб подправить за собой — считается плохо? Ведь раз лезешь в кишки компонента — значит берёшь на себя ответственность за его новое состояние и поведение. Поэтому чтоб такого не было — и имеет смысл делать их небольшими.

                                            Что касается двух уровней вложенности > >, то конечно я так не делаю. Это как раз первый признак того что надо добавить класс и делать ещё один компонент, более маленький.

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

                                            Пример:
                                            Кнопка с свг-иконкой и текстом в спане (допустим без спана обойтись нельзя). Определяем что это всё будет компонентом. Даём какой-нибудь корневой класс, например .main_button. Так вот зачем мне дополнительно давать классы свг и спану, если я могу написать так:

                                            .main_button{
                                                ...
                                                ...
                                                ...
                                                &>span{...}
                                            
                                                &>svg{...}
                                            
                                                &:hover,
                                                &:focus{...}
                                            
                                                &:active{...}
                                            
                                                &:disabled{...}
                                            
                                                &.js_showSpinner{....}
                                            
                                                @media при необходимости
                                            }
                                            


                                            И это будет полноценный компонент кнопки, который не следует потрошить, а взять и копипастнуть куда надо.
                                              0

                                              Судя по всему проекты — это сайты, где нет большого количества сторонних компонентов и плагинов, и команда не сильно большая.


                                              Но если есть комопонент, и ты его создатель, а вас еще 20+ человек на проекте и каждому необходимы какие то изменения в компонентах, тогда ты становишься на проекте узким горлышком, потому что только ты можешь делать измененя в компонентах, потому что это твоя отвтетственность.

                                                –1
                                                Верно — сайтики и команда небольшая.

                                                Создатель хорошо сказано. Компонент свёрстан, это моё произведение труда так сказать. Я ожидаю, что им будут пользоваться разработчики, а не будут его ломать. Потому что ломать его — это должно прийти «распоряжение» от проектировщика интерфейса, например поменялось состояние или поведение.

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

                                                И да, если вдруг разработчику нетерпится что- то изменить в разметке компонента, он это делает но и без проблем может найти место в стилях по корневому классу компонента чтоб дописать их. Если влом лезть в стили, может прямо инлайново написать их как-нибудь, верстальщик за ним подправит потом, ничего плохого в этом не вижу. Но это редкая ситауция, потому как «архитектура» вёрстки продумывается достаточно хорошо.

                                                И никаким узким горлышком вёрстка у нас никогда не была. Bottle neck — это как правило сам бизнес..))
                                            0
                                            Смотреть приятно, а производительность ваша снижается, командная работа затрудняется, любые изменения все ломают и нужно лезть в CSS и менять все теги-вложенности. Если проект постоянно находится в состоянии изменения (как большинство живых проектов), использование такого подхода является плохой идеей.
                                      +1
                                      button--positive, button--negative и button--neutral
                                      В данном случае использовать булевы модификаторы плохо, потому что это даёт возможность добавить блоку все три эти модификатора одновременно.

                                      Тут лучше использовать модификатор ключ-значение, потому что фактически может быть только один из этих модификаторов у конкретной кнопки, т.е. кнопка может быть или positive, или negative, или neutral.

                                      Рассматривайте это как аналог атрибута type у input. Значение конкретного типа определяет каким именно будет этот input. Тут логика точно такая же.
                                        0
                                        Можете привести пример, как избежать одновременного добавления этих модификаторов?
                                          0
                                          Использовать шаблонизатор, который знает, что нельзя на одном DOM-узле создавать классы одного и того же модификатора с разным значением.

                                          Например, BEMHTML.
                                          0
                                          Согласен, выше уже писали, правильнее будет button--type_positive.
                                          –1

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

                                            0
                                            В нашей сфере постоянное обучение и развитие является необходимостью. Иначе, как вы сами написали, к сожалению, будет не о чем говорить с другими специалистами. Что касается получения на выходе того же самого, то это лишь внешне то же самое. Рекомендую, если интересно, почитать о БЭМ и других методологиях, чтобы глубже разобраться в причинах их необходимости. Опять же, в одиночку особого смысла ни в чем таком нет, т. к. вы сами копаетесь в своем проекте. А вот в командной работе все недостатки очень быстро становятся очевидными.
                                              0
                                              У всех амбиции зашкаливают и принуждают применять методологии вместо того чтобы просто работать…
                                                +1
                                                Дело не в амбициях. Методология помогает выстроить процесс, а если у вас команда (иногда еще и распределенная), то «просто работа» без методологии заведет вас в тупик
                                            +2
                                            Полсотни комментариев, а автору спасибо за статью никто не сказал…
                                            Мне казалось, что я использую БЭМ, а оказалось, что только казалось. Прочитал и как-то всё по полочкам разложилось.
                                            В общем, спасибо за статью!

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

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