Пишем БЭМ правильно

#FrontendWeekend, August 2015

Igor Zenich, EPAM, ex. iDeus

Пишем БЭМ правильно

В исходниках этой презентации спрятались подсказки!

В комментах html написаны тезисы доклада и комментарии к слайдам требующим пояснений.

Презентация: delka.github.io/talks/2015/frontendweekend-bem/

iDeus
Зачем?

ещё один доклад про БЭМ…

БЭМ — семантичный!

Что он несёт?!

Pepelsbey

Имена БЭМ-классов создают дополнительный уровень логики

Есть DOM-дерево, а есть БЭМ-дерево.

<input class="big_red_button">
<input class="big_red_button order-button">
<input class="order-button discount-checkout__submit">
        

О чём будем говорить?

  1. Как вручную сверстать «по-БЭМ»
  2. Как не писать классы типа device-template-generic-indicator-control-slider-bar-d :)

Кстати, а зачем нужен БЭM?

Код, который тяжело поддерживать

Что в этом коде относится к классу user ?

<div class="media user premium">
  <img class="img photo avatar" src="" />
  <p class="body bio">...</p>
</div>
    

… и код, который ЛЕГКО поддерживать

Если мы перепишем этот код на BEM CSS, то все будет понятно просто из имён классов!

<div class="media user--premium">
  <img class="media__img user__photo avatar" src="" />
  <p class="media__body user__bio">...</p>
</div>
    

BEM CSS *

* CSS подмножество БЭМ

Full stack BEM

bemjson, bemhtml, bem-tools, enb, вот это вот всё…

Full stack BEM?

Когда он будет вам нужен — вы это сами поймёте.
Это будет момент, когда вам надоест писать html руками и вы захотите его генерировать.

Пример верстки по БЭМ

Изначальная вёрстка →

Похоже на БЭМ, да?

Давайте приглядимся

Это был пример неправильного BEM CSS

Повышение специфичности

:(

bit.ly/not-bem

Непонятность БЭМ

Даже разработчики Google Material Design не смогли с первого раза правильно написать имена классов по БЭМ :)

Методология не менялась

.block

Независимый блок

НБ или просто блок, это самодостаточный элемент страницы, который при перемещении в другое место на странице или на другую страницу не теряет своей самодостаточности.

БЭМ.Форум, Независимый блок

Обновленное определение блока

Логически и функционально независимый компонент страницы, аналог компонента в Web Components. Блок инкапсулирует в себе поведение (JavaScript), шаблоны, стили (CSS) и другие технологии реализации. Независимость блоков обеспечивает возможность их повторного использования, а также удобство в разработке и поддержке проекта.

bem.info, Методология

Правила независимости блока

  1. для описания элемента используется class, но не id
  2. каждый блок имеет префикс
  3. в таблице стилей нет классов вне блоков
БЭМ.Форум, История создания БЭМ (часть первая)

Как его таким написать?

Просто писать стили тупо на каждый блок.

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

А с препроцессорами БЭМ позволяет писать еще и очень чистый и логичный код.

Как проверить?

Просто навести на блок в инспекторе кода.
У него не должно быть каскада.

На самом деле каскад допускается, но его следует избегать.

.block__element

Элемент

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

bem.info, Методология

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

DOM-дерево

<ul>
  <li>
    <a>
      <span></span>
    </a>
  </li>
</ul>
    

DOM-дерево

.ul {}
.ul > li {}
.ul > li > a {}
.ul > li > a > span {}
    

БЭМ-дерево

<ul class="menu">
  <li class="menu__item">
    <a class="menu__link">
      <span class="menu__text"></span>
    </a>
  </li>
</ul>
    

БЭМ-дерево

.menu {}
.menu__item {}
.menu__link {}
.menu__text {}
    

БЭМ дерево — чистая логика

БЭМ-дерево не зависит ни от чего, даже от размещения в документе.

БЭМ-дерево не привязано к визуальному представлению блоков, оно отображает только логику, это и есть новый уровень семантики!

.block__el__el__el

Так #b_ поняли и используют за рубежом

Да и не только за рубежом, чё уж греха таить…

Я тоже раньше так писал

      .form-buy-results__to-city__slider__tab__column_buy
    

Так делать нельзя

Как надо?

<div class="block">
    <div class="block__elem1">
        <div class="block__elem2"></div>
    </div>
    <div class="block__elem3"></div>
</div>
    

А в CSS

.block {}
.block__elem1 {}
.block__elem2 {}
.block__elem3 {}
    

Элемент у элемента?

Если вам нужно сделать элемент у элемента, значит вам нужно:

Есть 2 варианта как это переписать

<div class="block">
    <div class="block__elem1">
        <div class="block__elem1__elem2"></div>
    </div>
</div>
    

1. Бить на блоки!

Делать новый блок

<div class="block1">
    <div class="block1__elem1 block2">
        <div class="block2__elem1"></div>
    </div>
</div>
    

CSS будет линейным:

.block1 {}
.block1__elem1 {}
.block2 {}
.block2__elem1 {}
    

2. Рубить ветки!

Делать БЭМ-дерево с одноуровневой вложенностью элементов

<div class="block1">
    <div class="block1__elem1">
        <div class="block1__elem2"></div>
    </div>
</div>
    

CSS по-прежнему будет линейным:

.block1 {}
.block1__elem1 {}
.block1__elem2 {}
    

Типичная ошибка

Попытка вложить имя элемента в имя блока

Чтоб «схитрить» и «как-будто не вложить», написать не .block__el1__el2 а .blockel1__el2 или .block__el1el2. Так нельзя.

.block {}
.blockel1 {}
.blockel1__el2 {}
    

Будут проблемы при переносе

Будут проблемы при переносе

Попытались перенести «странный элемент» в другое место - получили элемент что завис «в воздухе» без блока-родителя

<div class='someblock'>
    <div class='blockel1__el2'></div>
</div>
    

Так можно делать только если .blockelem сохранит логический смысл при переносе в другой блок.

element > element нельзя в CSS, но можно в HTML!

Обратите внимание - вы не можете вкладывать элементы в элементы в CSS, но можете и должны вкладывать элементы в элементы в HTML! DOM-дерево и БЭМ-дерево могут быть разными.

Запрет есть исключительно про нейминг!

БЭМ-дерево на то и дерево, что поддерживает вложенность, поэтому в БЭМ-дереве, разумеется, разрешается вкладывать элементы в элементы, блоки в блоки, блоки в элементы.

Vladimir Grinenko, @tadatuta

Хватит!
Мы не за теорией сюда пришли.

Бьём на максимально атомарные блоки!

NB: это и есть суть БЭМ, то, что многие не понимают.

Погружаясь вглубь DOM, нужно стараться создавать новые и новые блоки, а не строить связи родитель__элемент__элемент.

Вспоминаем как мы это делаем:

<div class="block1">
    <div class="block1__elem1 block2">
        <div class="block2__elem1"></div>
    </div>
</div>
    

HTML: как правильно вкладывать блоки в блоки?

<div class="b-blog">
    <div class="b-blog_item b-post">
    </div>
</div>
        

Миксование создаст связь между блоками!

В этой DOM-ноде смешиваются стили от 2-х разных блоков:

Эти стили объединяются в одном html-элементе и создают таким образом связи между блоками.

CSS: как правильно вкладывать блоки в блоки?

.b-blog {
  // блок 
  @at-root .b-post {
    // ещё один блок 
  }
}

Скомпилируется в:

.b-blog {
  /* стили блока */
}
.b-post {
  /* ещё один блок */
}

Мы написали вариант #1: микс элемента родителя + новый блок

Позиционирование — на элементе родителя и блоке родителя, стили блока — на новом блоке.

<div class="b-blog">
    <div class="b-blog_item b-post">
    </div>
</div>
        

Как можно было по-другому?

#2: Микс блоков и сетки

Позиционирование — на классах сетки, стили блоков — на самих блоках.

<div class="b-blog b-grid">
    <div class="b-grid__col b-post">
    </div>
</div>
        

#2.1: Не жалеем div: блоки внутри сетки

Позиционирование — на классах сетки, стили блоков — на самих блоках.

<div class="b-blog">
    <div class="b-grid">
        <div class="b-grid__col">
            <div class="b-post">
            </div>
        </div>
    </div>
</div>
        

#3: Блоки-врапперы (l-, h-)

Позиционирование — на блоках-врапперах.
Связей между блоками (блок—элемент) больше нет!

Яндекс НЕ рекомендует! Но этот вариант часто встречается в жизни.

<div class="l-blog">
    <div class="b-blog">
        <div class="l-post">
            <div class="b-post">
            </div>
        </div>
    </div>
</div>
        

Можно добавить микроформатов/микроданных

Это не нужно для БЭМ, просто я люблю микроформаты, Гугл любит микрофрматы и Яндекс тоже любит микроформаты :) Классно что мы можем добавлять любые имена классов куда-угодно, все стили у нас — только на БЭМ-классах.

Мы написали BEM-дерево:

<div class="block1">
    <div class="block1__elem1 block2">
        <div class="block2__elem1"></div>
    </div>
    <div class="block1__elem2"></div>
</div>
    

Нет каскада:

.block1 {}
.block1__elem1 {}
.block1__elem2 {}
.block2 {}
.block2__elem1 {}
    

Модификация

6 видов

  1. Модификатором
    • модификатором блока
    • модификатором элемента
  2. Контекстом (т.е. каскадом от блока выше)
  3. Уровнем переопределения (добавлением-перезаписью файла стилей)
  4. Миксованием (добавлением классов других блоков)
    • включая глобальный класс

Просто добавляйте модификатор!

<div class="block-name__elem_key_value">
    

А для элементов — делай каскад от модификатора.

Модификаторы для элементов, можно?

Если речь идет о простых правках, типа «активный пункт меню», то да, можно:

<a class="menu__link menu__link_state_active">
    

Булевые модификаторы

Кстати в таких простых случаях, можно писать модификаторы просто одним словом:

<a class="menu__link menu__link_active">
    

Но подумайте, может это новый блок?

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

БЭМ допускает ошибки

Самые популярные ошибки

1. block__el__el

Например, слайдеры очень часто верстают дикой вложенностью.

2. Повышение специфичности

В html как-будто всё ok:

<div class="block">
  <div class="block__el">
    

А на деле сели в машину и сгорели:

/* CSS */
.block .block__el {}
    

3. Стили вне блоков

<ul class="menu checkoutForm big myfuckingclass-bold">
    

Почему это ошибки?

Да потому что из-за этого потом тяжелей вносить правки и сложно переместить блок в другое место.

Классификация ошибок BEM CSS

от Александра Корецкого @n2j7 из Prom.ua

Проблемы со стилями:

Ошибки разработчиков:

Ошибки разработчиков:

Как мне?...

Вывести текст из WYSIWYG?

Как назначаются стили для типографики? Не будешь же назначать каждому тегу какой-то класс?

Artur Kornakov, @fliptheweb

Добавить .b-text блоку родителю

И использовать каскад.

.b-text h2 {}
.b-text p {}
.b-text img {}
.b-text ul li {}
    

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

Как писать BEM CSS в Sass

.b-list {
  /* стили блока  */
  //…
  &__item {
    /* стили элемента */
  }
  &__link {
    /* элемент #2… */
  }
}

Скомпилируется в:

.b-list {
  /* стили блока  */
}
.b-list__item {
  /* стили элемента */
}
.b-list__link {
  /* элемент #2… */
}

Sass: модификаторы блоков

.b-list {
  /* стили блока */
  &--style_numered {
    /* стили модифицированного блока */
  }
}

Скомпилируется в:

.b-list {
  /* стили блока  */
}
.b-list--style_numered {
  /* стили модифицированного блока */
}

Sass: модификаторы элементов

.b-list {
  /* стили блока */
  &__item {
    /* стили элемента */
    &--active {
      /* стили модифицированного элемента */
    }
  }
}

Скомпилируется в:

.b-list {
  /* стили блока */
}
.b-list__item {
  /* стили элемента */
}
.b-list__item--active {
  /* стили модифицированного элемента */
}

Sass: модификация элемента от модификатора блока

.b-list {
  $rootParent: &;
  &__item {/* обычный элемент */}
  &--style_numered {/* модифицированный блок */
    #{$rootParent}__item {
      /* элемент модифицированного блока */ 
    }
  }
}

* Это разрешённая практика в БЭМ, но изменение стилей не должно быть монструозным.

или вложим эти стили в сам элемент…

.b-list {
  $rootParent: &;
  &__item {
    /* обычный элемент */
    #{$rootParent}--style_numered & {
      /* элемент модифицированного блока */ 
    }
  }
}

* Это разрешённая практика в БЭМ, но изменение стилей не должно быть монструозным.

Скомпилируется в:

.b-list {
  /* стили блока */
}
.b-list__item {
  /* стили элемента */
}
.b-list--style_numered b-list__item {
  /* стили элемента модифицированного блока */
}

Модификация элемента элементом? bad practice, но…

.b-list {
  $rootParent: &;
  &__item {/* элемент */}
  &__link {/* элемент */
    #{$rootParent}__item--active & {
      // наглый модификатор элемента 
    }
  }
}

* Так делать неправильно. Модификация элемента элементом — нарушение правил! Но бывает нет выхода.

Скомпилируется в:

.b-list {/* блок */}
.b-list__item {/* элемент */}
.b-list__link {/* элемент */}
.b-list__item--active .b-list__link {
  /* наглый модификатор элемента */
}

Как «правильно» модифицировать блоки (внешний вид) от контекста?

  1. Можно юзать каскад, но это тонкая работа — для случая «перебить чуть-чуть стилей, ведь этот блок нам не понадобится переносить в изменённом виде куда-то отдельно».
  2. Добавить модификатор блоку — просто и надежно, но не создает логической связи между блоком-родителем и вложенным в него блоком, что должен изменятся.
  3. Использовать миксование: создать элемент родителя, навесить на него нужные стили и смиксовать с вложенным блоком.

Спрашивайте: ru.bem.info/forum

Пример переверстки по БЭМ (упрощенный)

Диалекты БЭМ

Вот это вот всё на 5 минут:

Префиксы

b-,
l-,
g-,
i-,
h-,
m-,
c- и js-,
qa-,
o-,
c-(другой :),
u-,
t-,
s-,
is-,
has-

Префиксы

Были в раннем БЭМ. Сейчас пропагандируются Гарри Робертсом и используются многими не-Яндекс разработчиками. Используются для создания своего пространства имен и логического разделения блоков.

BEMIT: Пространства имен

Продвинутое использование префиксов и суффиксов от Гарри Робертса. Попытка описать взаимосвязь между независимыми блоками с точки зрения SMACSS и OOCSS.

Стиль Гарри Робертса

Многим нравится зарубежный формат модификаторов, через „--“, он читабельней.

<a class="block-name__element-name--state_active">

Стиль camelCase

А через camelCase – ещё читабельней!

<a class="blockName__elementName--state_active">

Стиль без подчеркиваний

Некоторые идут ещё дальше и заменяют „__“ на „-“. camelCase единственный гарантирует что вы поймете где блок, а где элемент.

<a class="blockName-elementName--state_active">

Сокращенные модификаторы

Сокращенные модификаторы

Правильно писать так:
.block-name__elem_key_value

Или так:
.blockName__elem--key_value

Но такие имена классов тяжело читать

<div class="block-name block-name_key1_val1 block-name_key2_val2 block-name_key3_val3">

Нет дуракоустойчивости, модификатор могут перенести без блока:

<div class="block-name_key1_val1 some-another-block">

Хочется так:

<div class="block-name -key1_val1 -key2_val2 -key3_val3">

Сокращенные модификаторы

За рубежом их назвали „Individual modifiers: a shorter syntax“. У нас перевели как «Обособленные модификаторы: сокращенный синтаксис». Яндекс в официальной документации называет их «Стиль No-namespace».

<div class="blockName__elem -key_value">
.blockName {
  &__elem {
    &.-key_value {
    }
  }
}
    

— Теряем миксы блоков с модификаторами!
— Не можем использовать Full BEM Stack!

Решением может быть запись модификаторов в столбик

<div class="block-name block-name_key1_value1 block-name_key2_value2 block-name_key3_value3">
<!-- VS -->
<div class="block-name
            block-name_key1_value1
            block-name_key2_value2
            block-name_key3_value3">

А ещё можно юзать Jade, а в Яндексе вообще не пишут html-код руками, у них BEMJSON.

JS-блоки

$('.js-fancybox').fancybox();
    
Это миксование css-блока и js-блока на одной dom-ноде. Канонический БЭМ считает, что они не нужны, т.к. js-функционал нет смысла отделять от блока. Гарри Робертс и не-Яндекс разработчики их активно используют и пропагандируют: т.к. разделение позволяет легко копировать css-блок без связанного с ним JS.

Альтернативные реализации: OPOR

Известный пример использования методологии БЭМ сторонним разработчиком - Артёмом Сапегиным

Альтернативные реализации: BEViS

Диалект БЭМ, с более строгими правилами для максимальной надежности верстки, придуманный Вадимом Макишвили для Яндекс.Карт.

Альтернативные реализации:
Google MDL

Библиотека блоков от Google, css-реализации их Material Design придуманного для Android.

Full BEM Stack в HTML

Вы можете создавать свои гайдлайны

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

Это всё БЭМ

Тонкости БЭМ

БЭМ включает в себя много техник

Кое-что мы с вами обсудили/упомянули:

Но многое осталось за кадром

Читать дальше

…и дальше

Спасибо!

Igor Zenich
EPAM, ex. iDeus

Презентация: delka.github.io/talks/2015/frontendweekend-bem/
Видео доклада: youtube.com/watch?v=kBgHdSOj33A

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

Powered by Shower