Содержание

БЭМ

Sass, LESS, stylus, PostCSS...

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

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

Золотые правила

Cоблюдайте предложенные здесь или свои собственные соглашения.

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

  1. Весь код проекта должен выглядеть так, словно он написан одним человеком, вне зависимости от количества разработчиков.
  2. Делать нужно настолько просто, насколько возможно. Но не проще.
  3. Любой инструмент при бездумном использовании может выстрелить в ногу.

БЭМ (как метод именования селекторов)

Зачем

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

Блок — это самостоятельная часть страницы

<!-- .product — БЭМ-блок -->
<div class="product">
  ...
</div>

Блоки можно и нужно вкладывать друг в друга

<!-- .page-header — БЭМ-блок -->
<div class="page-header">
 
  <!-- .logo — вложенный БЭМ-блок -->
  <a class="logo">...</a>
 
</div>

Элемент — часть БЭМ-блока

<div class="product">
 
  <!-- product__image — БЭМ-элемент блока product -->
  <img class="product__image" src="..." alt="...">
 
  <!-- product__description — БЭМ-элемент блока product -->
  <p class="product__description">...</p>
 
  <!-- product__more-link — БЭМ-элемент блока product -->
  <a class="product__more-link" href="">...</a>
</div>

Элемент можно использовать вне его блока только в исключительных случаях

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

Пример: расположение попапа-элемента не внутри блока-родителя, а в самом конце DOM (дабы показывать попап независимо от ограничения видимости родителей).

Если Вы используете БЭМ только как метод именования селекторов, при написании разметки никогда не используйте элемент вне блока.

Почему нельзя просто так располагать элемент вне блока:

Элементов может не быть

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

<!-- Нет ошибки: блок без элементов -->
<a class="btn" href="">Скачать</a>

Как отличить БЭМ-блок и БЭМ-элемент

Просто задайте себе вопрос: «Эта сущность может потребоваться мне отдельно, сама по себе? Или она нужна только внутри её родителя?» Если нужна отдельно — это БЭМ-блок, если мыслима только внутри родителя — это БЭМ-элемент.

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

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

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

Некоторые фрагменты дизайна — всегда БЭМ-блоки

Модификатор — дополнительный класс для смены оформления или поведения

<!-- .product--large — БЭМ-модификатор -->
<div class="product  product--large">
  ...
</div>

Модификатор нельзя использовать самостоятельно

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

<!-- Нет ошибки: модификатор сопутствует модифицируемому классу -->
<div class="product  product--large">
  ...
</div>
 
<!-- ОШИБКА: модификатор без класса, который он модифицирует -->
<div class="product--sale">
  ...
</div>

Миксование — комбинирование на одном теге классов БЭМ-блока и БЭМ-элемента

Комбинация возможна в любом сочетании: БЭМ-блок + БЭМ-элемент, БЭМ-блок + БЭМ-блок, БЭМ-элемент + БЭМ-элемент. Этот подход позволяет:

Классы БЭМ-блоков следует писать первыми.

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

<div class="page-header">
  <div class="page-header__nav-wrap">...</div>
  <a class="btn  page-header__btn" href="/buy">...</a>
</div>
<div class="slider">
  <div class="promo  slider__inner">
    <div class="promo__item  slider__item">
      ...
    </div>
  </div>
</div>

Один БЭМ-блок = один файл

В файловой системе при работе с CSS-препроцессорами каждый БЭМ-блок должен быть описан в своём отдельном файле.

// файл slider.less
.slider {
  ...
}
 
// файл promo.less
.promo {
  ...
}

БЭМ-дерево плоское, в отличие от DOM

В классах БЭМ-элементов нельзя прописывать иерархию (два и более сегмента __ недопустимы).

<div class="promo">
  <div class="promo__description">
    <!-- Нет ошибки: вложенный элемент не имеет особенностей -->
    <a class="promo__link" href="">...</a>
  </div>
</div>
 
<div class="promo">
  <div class="promo__description">
    <!--ОШИБКА: попытка прописать иерархию -->
    <a class="promo__description__link" href="">...</a>
  </div>
</div>

Sass, LESS, Stylus, PostCSS...

Файловая организация

# Один из вариантов файловой организации (удобно должно быть ВАМ)
src/                   # папка с исходниками, из которых собирается проект
  blocks/              # папка с БЭМ-блоками
    page-header/       # папка БЭМ-блока
      img/             # картинки, используемые в БЭМ-блоке
      page-header.less # стилизация БЭМ-блока
      page-header.html # разметка БЭМ-блока (не обязательно)
      page-header.js   # доп. интерактив БЭМ-блока (не обязательно)
    page-footer/       # папка БЭМ-блока
    logo/              # папка БЭМ-блока
    ...
  less/
    mixins/            # папка с примесями
    style.less         # диспетчер подключений (только импорты)
    variables.less     # переменные проекта

@import

// style.less
@import 'variables.less';           // правильно
@import 'mixins.less';              // правильно
 
@import '../blocks/page-header/page-header.less';   // правильно
@import '../blocks/page-footer/page-footer.less';   // правильно
 
@import 'responsive-imports.less';  // неправильно, если внутри этого файла есть импорты
@import 'top-part-page.less';       // неправильно, если нет такого БЭМ-блока
 
// неправильно, импорт внутри @media
@media screen and (min-width: @laptop-min) {
  @import '../blocks/promo/promo.less';
}

Вложения селекторов

.promo {
  display: block;
                    // пустая строка для улучшения читаемости
  a {               // первый уровень вложенности
    color: #ff0000;
                    // пустая строка для улучшения читаемости
    &:hover {       // не увеличивает уровень вложенности
      ...
    }
  }
}

@media

.promo {
  display: block;
  
  // Хорошо: условие очевидно, в переменной только ширина
  @media (min-width: @screen-md) {
    display: none;
  }
  
  @media (min-width: @screen-lg) {
    display: block;
  }
 
  // Плохо: условие целиком вынесено в переменную
  @media (@mobile-width) {
    display: block;
  }
}

Амперсанд

.promo {
  
  // Правильно: амперсанд перед псевдоклассом
  &:hover { ... }
  
  // Правильно: амперсанд перед разделителем элемента
  &__item {
  
    // НЕПРАВИЛЬНО: амперсанд в месте разделения словосочетания в названии элемента
    &-link { ... }
  }
  
  // НЕПРАВИЛЬНО: амперсанд в месте разделения словосочетания в названии блока
  &-shover { ... }
  
  // Правильно: модификаторы нужно писать под элементами
  &--large { ... }
  
}

Очередность написания в контексте селектора

В контексте селектора используйте следующую очередность:

  1. Стилевые правила для этого селектора.
  2. Вложенные сторонние селекторы.
  3. Псевдоселекторы и псевдоэлементы.
  4. @media этого контекста.
  5. БЭМ-элементы.
  6. БЭМ-модификаторы.
.page-header {
  position: relative;
  display: block;
  
  // Этот блок простилизован в другом файле, тут только каскадная модификация
  .fp-tableCell { ... }
  
  &:before { ... }
  
  @media (min-width: @screen-lg) { ... }
  
  &__item {
    display: block;
  
    &:before { ... }
  
    @media (min-width: @screen-md) { ... }
  }
  
  &--large {
  
    .page-header__item { ... }
  
    @media (min-width: @screen-md) { ... }
  }
  
}

Переменные

@color-main:                  #0275d8;
@color-success:               #5cb85c;
@color-danger:                #d9534f;
 
@text-color:                  @gray-darkest;
@text-color--muted:           @gray;
 
@font-size:                   1.6rem;
@font-size--h1:               3rem;
...
@font-size--small:            0.75em;
 
@line-height:                 1.375em;
 
@font-family:                 'Roboto', Arial, sans-serif;
 
@screen-xs:                   0;
@screen-sm:                   480px;
@screen-md:                   768px;
@screen-lg:                   992px;
@screen-xl:                   1200px;

Примеси