Список рекомендаций по неусложнению жизни себе и другим участникам проекта по вёрстке.
Если у вас есть что дополнить или вы хотите принять участие в разработке этих соглашений, пожалуйста, создайте issue на GitHub или форкните репозиторий и пришлите pull request.
Все нижеследующие соглашения выработаны автором на основании опыта веб-разработки с 2000 г., призваны максимально облегчить и ускорить чтение, понимание и модификацию вёрстки.
Cоблюдайте предложенные здесь или свои собственные соглашения.
Есть ситуации, когда правила нужно нарушить. Причина должна быть действительно серьезной, не предположительной.
Само понятие БЭМ — не только метод именования селекторов, но парадигма восприятия проекта как набора сущностей (блоки, элементы, модификаторы). Полный стек БЭМ подразумевает двойную шаблонизацию и имеет относительно высокий порог входа. Используйте БЭМ хотя бы как способ именования селекторов.
<!-- .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>
Комбинация возможна в любом сочетании: БЭМ-блок + БЭМ-элемент, БЭМ-блок + БЭМ-блок, БЭМ-элемент + БЭМ-элемент. Этот подход позволяет:
.btn
внутри .page-header
необходим внешний левый отступ в 37 пикс. Можно дописать для тега с .btn
дополнительный класс .page-header__btn
и дать отступ с помощью этого селектора. Это нормальная практика, её можно спокойно использовать..article
и для .page-footer__section
шрифтовые свойства одинаковы. Можно вынести определение шрифтовых свойств в новый блок .text
и дописать этот класс на .article
и .page-footer__section
. Этот подход излишне связывет части страницы (напоминает OOCSS и класс-хелпер), не делайте так..product
, но каждому элементу потока нужны стилевые свойства ячеек модульной сетки (по которой выстроен потоковый вывод). Можно добавить для .product
класс ячейки модульной сетки, что бы не делать обертку с этим классом. Это чревато конфликтом отступов/размеров, не смешивайте на одном теге классы обёртки и содержимого.Классы БЭМ-блоков следует писать первыми.
Миксование несколько ухудшает восприятие кода и увеличивает вероятность ошибки смешения стилей, при которой вы пишите стилевое правило в контексте не того селектора, где оно реально нужно.
<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.scss
.slider {
...
}
// файл promo.scss
.promo {
...
}
В классах БЭМ-элементов нельзя прописывать иерархию (два и более сегмента __
недопустимы).
<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>
# Один из вариантов файловой организации (удобно должно быть ВАМ)
src/ # папка с исходниками, из которых собирается проект
blocks/ # папка с БЭМ-блоками
page-header/ # папка БЭМ-блока
img/ # картинки, используемые в БЭМ-блоке
page-header.scss # стилизация БЭМ-блока
page-header.html # разметка БЭМ-блока (не обязательно)
page-header.js # доп. интерактив БЭМ-блока (не обязательно)
page-footer/ # папка БЭМ-блока
logo/ # папка БЭМ-блока
...
less/
mixins/ # папка с примесями
style.scss # диспетчер подключений (только импорты)
variables.scss # переменные проекта
mixins.scss
).// style.scss
@import 'variables.scss'; // правильно
@import 'mixins.scss'; // правильно
@import '../blocks/page-header/page-header.scss'; // правильно
@import '../blocks/page-footer/page-footer.scss'; // правильно
@import 'responsive-imports.scss'; // неправильно, если внутри этого файла есть импорты
@import 'top-part-page.scss'; // неправильно, если нет такого БЭМ-блока
// неправильно, импорт внутри @media
@media screen and (min-width: $laptop-min) {
@import '../blocks/promo/promo.scss';
}
@media
..promo {
display: block;
// пустая строка для улучшения читаемости
a { // первый уровень вложенности
color: #ff0000;
// пустая строка для улучшения читаемости
&:hover { // не увеличивает уровень вложенности
...
}
}
}
@media
в селекторы, а не наоборот.@media
друг в друга.@media
-условия max-width в пользу min-width.@media
рядом, не пишите селекторы между ними.@media
там, где используете @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 { ... }
}
В контексте селектора используйте следующую очередность:
@media
этого контекста..page-header {
position: relative;
display: block;
@media (min-width: $screen-lg) { ... }
&:before { ... }
// Этот блок простилизован в другом файле, тут только каскадная модификация
.fp-tableCell { ... }
&__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;
.clearfix();
).@media
).