Не используйте заглавные буквы и camelCase. Слова отделяйте через дефис.
Плохо
.accordionMenu {}
Хорошо
.accordion-menu {}
БЭМ - это целая методология, со своими инструментами. Но мы используем только схему именования по БЭМ. Разбиваем разметку на блоки (компонентный подход), в них выделяем элементы. Чтобы изменить состояние блока или элемента - добавляем модификаторы.
Плохо
.menu-item-active {}
Хорошо
.menu__item_active {}
Для выделения элемента в имени класса используйте __
(два подчеркивания).
Для *модификатора - _
(одно подчеркивание)
Плохо
.menu--item-text
.menu__item--text
.menuItem-text
Хорошо
.menu__item_text
В большинстве случаев из названия и так понятен его тип. Однако будет полезно почитать о БЭМ-префиксах
Плохо
.b_banner {}
.l_cards {}
Хорошо
.banner {}
.card-list {}
Не повторяйтесь,
не пишите в каждом элементе или модификаторе название. Используйте оператор &
из
scss,
но не увлекайтесь. Всегда сокращайте элемент, но не состояние элемента, таким
образом состояние будет проще найти в сотне уже написаннх файлов. Выглядит это,
конечно, не так изящно, но зато в разы удобнее при разработке и поддержке
больших проектов. Исключением здесь является большое состояние блока или
элемента - его нужно выносить в отдельный раздел и писать название блока и
модификатор полностью.
Плохо
.menu__item {}
.menu__item_active {}
.menu {
&_level_1 {
/* много-много стилей и элементов */
}
}
.menu {
&__item {
&_active {} // Попробуйте найти такое состояние (_active) в большом проекте где >100 файлов стилей
}
}
Хорошо
.menu {
&__item {}
&__item_active {} // А это уже проще найти по маске __item_active
}
//Выносим большое состояние блока в отдельный раздел и пишем имя блока и модификатор полностью
.menu_level_1 {
/* много-много стилей и элементов */
}
Очень часто в HTML надо вложить элемент внутрь другого элемента. Не надо в этом случае писать полный путь до этого элемента, описывать всех его родителей. Внутри CSS структура элементов должна быть плоской.
Плохо
.menu__item__link {}
.footer__container__row__column {}
Хорошо
.menu__link {}
.footer {
&__container {}
&__row {}
&__column {}
}
Код с ними тоже трудно поддерживать. Обычно его пишут, когда нужно переопределить правило, но запутались в своих каскадах и не понимают как работает специфичность в CSS. Писать !important - это все равно что пытаться перекричать тех, кто говорит. А они начинают говорить еще громче, и все начинают орать и срывают горло.
Плохо
// переопределяем каскад из стилей для WYSYWIG-редактора
.article ul>li { list-style: dots; }
.card {
li { list-style: none!important; }
// Плохо. Если в другой теме стили надо отменить?
}
Хорошо
.card_article {
ul>li { list-style: none; }
}
Исключения из правила;
// надо отключить стили, проставленные сторонней js-библиотекой
.carousel_empty {
.slick { display: none; }
}
// сторонний виджет вставляет асинхронно добавляет стили, а в них
// написан !important
.ya-map { border: 0!important }
Код с CSS-каскадом очень сложно, просто чудовищно сложно поддерживать.
Приходится учитывать специфичность селектора. например, чтобы
переопределить такой css-селектор .menu>li>ul>li>a.first
придется написать
такой же длинный селектор .menu>li>ul>li>a.special
.
Плохо
.form .fieldset .input
Хорошо
.form {
&__input {}
}
Есть исключения из правила:
// внутри блока с модификатором
.card_inverted {
.card {
&__title { color: #fff }
}
}
// когда нужно переопределить стили сторонней js-библиотеки
.carousel {
.slick-arrow { fill: #000; }
}
)
// если содержимое блока берется из WYSYWIG-редактора (напр. ckeditor.js)
.article {
h1 { margin: 20px 0 10px; }
}
Со временем поменяется дизайн, и элемент перенесут в другое место. Или в новой
теме синие кнопки станут красными. Представляете как будет запутывать людей элемент
form__left
, который располагается справа?
Плохо
.btn_blue {}
.card__bottom {}
Хорошо
.btn_primary {}
.card__controls {}
Сокращения разные люди могут расшифровать по разному. Понятное вам может быть непонятно другим. Давайте названия так, чтобы через год вы могли легко понять, что имели ввиду.
Плохо
.s1 {}
.bl-cr {}
.card__d {}
.f__bl {}
Хорошо
.section_first {}
.car_primary {}
.card__description {}
Исключения
// Допустимые сокращения
descr - description
btn - button
anim - animation
Чтобы не было длинной портянки на 6000 строк, мы разбиваем стили на отдельные
файлы. Подключаем их в специальный индексный файл с помощью @import
.
В индексном файле нельзя вставлять никакие определения классов, переменных,
миксинов и т.п., только подключение других файлов.
Плохо
// файл main.scss
.header {}
.content {}
/* ...3000 строк кода... */
.footer {}
Хорошо
// файл main.scss
@import "header";
@import "content";
/* ...еще несколько @import... */
@import "footer";
Так людям и программам будет легче отличить в вложенный файл от индексного. Название индексного файла не начинайте с подчеркивания.
Плохо
// список файлов:
button.scss
content.scss
header.scss
main.scss
pagination.scss
Хорошо
_button.scss
_content.scss
_header.scss
_pagination.scss
main.scss
В одном файле может быть описан только один БЭМ-блок, даже если там будет всего 3 строчки года.
Так будет сразу понятно, что класс .filter__submit
описан в файле _filter.scss
.
Плохо
// файл _header.scss:
.header { /* стили */ }
.topbar { /* стили */ }
Хорошо
// файл _header.scss:
.header { /* стили */ }
// файл _topbar.scss:
.topbar { /* стили */ }
Чтобы легче искать файлы. Если раскладывать по папкам, то нужно вводить понятные правила для разделения.
Плохо
// Чем плохи эти правила разбиения на папки:
// * по разделам - компоненты могут использоваться в нескольких разделах
main-page
├───banner.scss
├───slider.scss
catalog
└───card.scss
// * по типу - не всем будет понятно, к какому типу относится компонент
layout
├───header.scss // можно поспорить и отнести шапку к компонентам
├───footer.scss
components
└───card.scss
Хорошо
styles
├───header.scss
├───footer.scss
└───card.scss
Если определять переменные в разных файлах, то велика вероятность переопределить значение из другого компонента. Отыскать нужное переменную тоже будет сложнее. Поэтому пишем в один файл.
Плохо
// файл button.css
$button-color: #ccc;
.button { color: $button-color; }
// файл input.css
// устанешь искать, где же опредение переменной $button-color
.input { color: $button-color; }
Хорошо
// файл _variables.css
$button-color: #ccc;
$input-color: $button-color;
// файл _input.css
.input { color: $input-color; }
Плохо
Хорошо
С этой задачей куда лучше справится autoprefixer. С ним легче добавить или убрать нужные префиксы, если изменится список поддерживаемых браузеров. Да и код будет выглядеть чище.
Плохо
.card {
// старомодные неуклюжие миксины
@include border-radius(4px);
// Некоторые ide автоматически генерируют вендорные префиксы
background: -webkit-linear-gradient(top, #87e0fd 0%, #05abe0 100%);
background: -moz-linear-gradient(top, #87e0fd 0%, #05abe0 100%);
background: linear-gradient(to bottom, #87e0fd 0%, #05abe0 100%);
}
Хорошо
.card {
border-radius: 4px;
background: linear-gradient(to bottom, #87e0fd 0%, #05abe0 100%);
}
Эта ненужная абстракция. Вы сэкономите полсекунды на набор, зато другой человек будет несколько минут разбираться, ходить по файлам, выясняя что имели ввиду.
Плохо
// ненужные сокращения в CSS
.card { @include fl-l; @include bd-def; }
// то же самое в HTML (привет Atomic CSS и одноклассникам.ру)
<div class="card fl-l bd-def"></div>
Хорошо
.card { float: left; border: 1px solid $border-default; }
<div class="card"></div>
Плохо
.card {
&__title {
@media (min-width: 480px) {}
/* ...еще несколько медиа-запросов... */
}
&_special { /* описание состояния */ }
@media (min-width: 768px) {
/* опять описание адаптивности */
}
}
Хорошо
// Общие стили
.card {
&__title {}
}
// Адаптивность
.card {
@media (min-width: 480px) {
&__title {}
}
@media (min-width: 768px) {
&__title {}
}
}
// Состояния
.card_special {}
Состояниями компонента считаются:
- псевдоклассы вроде :hover, :focus, :active, :first-child и т.д.
- модификаторы (&_active, &_special и т.д.)
Состояния описываются после общих стилей и адаптивности, чтобы этот раздел смог переопределить значения по-умолчанию.
Плохо
.card {
&__title { /*..*/ }
&_special {
&__title { /*..*/ }
&__media { /*..*/ }
}
&__media { /*..*/ }
}
Хорошо
.card {
&__title { /*..*/ }
&__media { /*..*/ }
}
// == Состояния
.card_special {
&__title { /*..*/ }
&__media { /*..*/ }
}
Когда я в первый раз делал мобильную версию сайта из старой десктопной, меня не покидала мысль «Проще бы было переписать сайт заново». Эту же боль испытывал Люк Роблевски, который придумал Mobile First - принцип создания интерфейсов, когда сначала делают для маленьких мобильных экранов, и только потом для мониторов настольных компьютеров.
Сперва может показаться, что так делать интерфейсы дольше и труднее. Это неправда. Нарисуйте раскладку для всех размеров экрана, продумайте разметку и стили - и все получится быстро и легко.
Плохо
В 4-й раз переделывать отображение для больших экранов, потому что порядок элементов
не подходит для мобильного вида.
Хорошо
Работать по такому алгоритму:
1. Нарисовать на бумаге, как будут располагаться "полочки" на разных экранах
2. Придумать, какими css-свойствоми это сверстать
3. Написать разметку и стили
Мы пишем код в том порядке, в котором будем его тестировать. Например, нам нужно поправить код для планшетов. Если нужные стили объединить под одним медиа-запросом, то нужно будет прыгать по всему файлу, они все рядом. Также в результате получится гораздо меньше кода.
Плохо
.teaser {
&__img {
@media (min-width: $screen-sm-min) {
/* много стилей */
}
@media (min-width: $screen-lg-min) {
/* много стилей */
}
}
&__text {
@media (min-width: $screen-sm-min) {
/* много стилей */
}
@media (min-width: $screen-lg-min) {
/* много стилей */
}
}
}
Хорошо
.teaser {
@media (min-width: $screen-sm-min) {
&__img { /* много стилей */ }
&__text { /* много стилей */ }
}
@media (min-width: $screen-lg-min) {
&__img { /* много стилей */ }
&__text { /* много стилей */ }
}
}
Вы сверстали интерфейс для больших экранов и начали переделывать его под маленькие. Первое, что вам придет в голову:
- Так, сейчас я напишу max-width:780px для планшетов, потом max-width:480px для телефонов. И буду как Лев Толстой.
Уже на стилях для планшетов, вы увидите, что интерфейс настольных компьютеров самый большой и самый сложный. В нём всякие вкладки, подписи к иконкам, большие картинки. Всего этого на планшетах и телефонах быть не должно. И вы начинаете отменять стили. Было 20 правил, написали еще 20, чтобы их отменить. Может проще их сразу не писать? Ограничить область видимости этих правил только на большие экраны? Для этого и был создан min-width для медиа-запросов.
Но запросы с max-width бывают полезными, если их правильно готовить. Подробнее читайте в следущем разделе
Плохо
.header {
/* стили для десктопной версии */
@media (max-width: 1023px) {
/* скроем ненужное для мобильных */
&__address,
&__phone {
display: none;
}
/* напишем стили для сендвича */
&__sandwich { /* .. */ }
/* отменим кучу десктопных стилей */
&__logo { width: 30px }
&__title { font-size: 10px; }
}
}
Хорошо
.header {
/* Напишем сначала стили для мобильных */
&__address,
&__phone {
display: none;
}
&__sandwich { /* .. */ }
&__logo { width: 30px }
&__title { font-size: 10px; }
/* напишем стили для десктопа */
@media (min-width: 1024px) {
&__logo { width: 100px; }
&__title { font-size: 20px; }
&__sandwich { display: none; }
}
}
По макету кнопка меню с иконкой сендвича будет только на телефонах. Можем написать все её стили, а потом скрыть еще одним медиа-запросом с min-width. Тогда мы напишем лишние стили. Лучше использовать запрос с max-width и изолировать стили на маленьких экранах.
Плохо
.sandwich { /* стили */ }
@media (min-width: $screen-md-min) {
.sandwich { display: none; }
}
Хорошо
@media (max-width: $screen-sm-max) {
.sandwich { /* стили */ }
}
Запросы с max-width описывают маленькие экраны. Мы пишем запросы в порядке возрастания размеров экрана, поэтому лучше написать сначала все запросы с max-width, а уже потом все с min-width.
Плохо
.credit-form {
@media (min-width: $screen-sm-min) { /* стили */ }
@media (min-width: $screen-xs-max) { /* стили */ }
@media (min-width: $screen-md-min) { /* стили */ }
@media (min-width: $screen-sm-max) { /* стили */ }
}
Хорошо
.credit-form {
@media (min-width: $screen-xs-max) { /* стили */ }
@media (min-width: $screen-sm-max) { /* стили */ }
@media (min-width: $screen-sm-min) { /* стили */ }
@media (min-width: $screen-md-min) { /* стили */ }
}
Не добавляйте margin на корень компонента. В другом месте у компонента будут другие оступы. Перенесите margin на элементы вышестоящего компонента.
Полочки и книжки. Представьте себе книжную полку. На ней стоят разные книжки. Все компоненты можно назвать "полками" или "книгами". Полки описывают местоположение. Им подходят стили margin, padding, display: flex, float. Для книг подходят стили оформления: color, background и другие. Не описывайте в блоках-полочках оформление, а в блоках полочках не описывайте местоположение.
Плохо
// На первом макете у вкладок были такие отступы
.tabs { margin: 30px auto 40px; }
// На втором они были поменьше
.tabs_in_filter { margin: 10px auto 0; }
// На третьем вкладки были выровнены по левому краю, а не по центру
.tabs_align_left { margin: 30px 0 40px; }
Хорошо
// Перенесём все отступы в элементы других компонентов
.slice__tabs { margin: 30px auto 40px; }
// На втором они были поменьше
.filter__tabs { margin: 10px auto 0; }
// На третьем вкладки были выровнены по левому краю, а не по центру
.shop__tabs { margin: 30px 0 40px; }
Элемент должен быть частью блока и не должен использоваться отдельно от него. Если элемент понадобился в другом блоке - превратите элемент в отдельный блок.
Плохо
// Цена в карточке
<section class="card">
<h3 class="card__title">Title</h3>
<div class="card__price">30 000</div>
</section>
// Оформление цены понадобилось вставить в шапку
<header class="header">
<img class="header__logo" src="logo.png">
<div class="card__price">30 000</div>
</header>
Хорошо
// Создадим новый блок для цены
<section class="card">
<h3 class="card__title">Title</h3>
<div class="card__price price">30 000</div>
</section>
// Используем цену где угодно:
<header class="header">
<img class="header__logo" src="logo.png">
<div class="header__price price">30 000</div>
</header>