Генерация названий селекторов в стиле БЭМ с помощью препроцессоров

Методология БЭМ никогда не подразумевала того, что длинные названия классов в селекторах типа .block__element_modifier будут записываться вручную. В Яндексе для этого сразу используются специальные утилиты, которые генерируют стили и разметку блока. Если вы используете препроцессоры для генерации CSS, то вам тоже не нужно каждый раз повторять названия блока или элементов. Сейчас я расскажу, как это делается.

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

.header {
    .title {
        font: bold 24px/1 sans-serif;
    }
}

После трансформации получаем CSS:

.header .title {
    font: bold 24px/1 sans-serif;
}

Вместо «родительской части» селектора можно использовать символ &. Удобно, когда нужно применить свойства и выбранному элементу, и его потомкам или псевдо-классам.

.header {
    a {
        &, &:hover, &:focus {
            color: white;
            text-decoration: none;
        }
    }
}

CSS:

.header a,
.header a:hover,
.header a:focus {
    color: white;
    text-decoration: none;
}

LESS (а с недавнего времени и SASS) подставляют в новый селектор вместо & часть родительского селектора без каких-либо изменений. Так же нет ограничений на последующие или предыдущие символы в новом селекторе. Поэтому вместо каскада можно генерировать простые селекторы.

.header {
    &__link {
        &, &:hover, &:focus {
            color: white;
            text-decoration: none;
        }
    }
}

CSS:

.header__link,
.header__link:hover,
.header__link:focus {
    color: white;
    text-decoration: none;
}

Аналогично формируем простые селекторы для модификаторов элементов.

.header {
    background: white;
    &__title {
        font: bold 24px/1 sans-serif;
        &_featured {
            font-size: 30px;
        }
    }
}

CSS:

.header {
    background: white;
}
.header__title {
    font: bold 24px/1 sans-serif;
}
.header__title_featured {
    font-size: 30px;
}

Вложенные селекторы превратились в простые.

Важно знать

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

Название блока используется только один раз

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

.block {
    // стили блока
}

Элементы идут на втором уровне вложенности

.block {
    // стили блока
    &__element {
        // стили элемента
    }
    &__title {
    }
}

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

.block {
    &__element {
        &-wrapper {
            // Пример плохого элемента.
            // Его будет трудно отыскать.
        }
    }
}

Никогда не делайте подобного. Такой селектор очень трудно будет найти в коде. В примере название элемента element-wrapper разорвано на две части. Пишите название элементов полностью даже если они частично повторяют уже существующие.

Псевдо-классы, псевдо-элементы и модификаторы элементов допускается писать на третьем уровне вложенности

.block {
    &__element {
        &_modifier {
            // стили модификатора элемента
        }
        &_modifier_value {
            // модификатор со значением не разбиваем на части
        }
        &:hover {
            // псевдо-класс — это тоже модификатор
        }
    }
}

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

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

.block {
    &__element_active {
        // модификатор элемента на втором уровне
    }
    &__element_modifier_good {
        // пример модификатора со значением
    }
}

На практике мне больше нравится именно такая запись — модификатор на втором уровне. Также считаю, что недопустимо разбивать название модификатора на части, а так же не стоит отделять значение модификатора от его названия. Излишняя структурированность может ухудшить читаемость. Легко потерять текущий контекст. Помните об этом.

Модификаторы блока могут участвовать в каскаде и располагаются на втором уровне

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

.block {
    background: white;
    &__title {
        color: black;
        font: bold 24px/1 sans-serif;
    }
    &_featured {
        background: black;
    }
    &_featured &__title {
        color: white;
        font-size: 30px;
    }
}

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

.block {
    background: white;
}
.block__title {
    color: black;
    font: bold 24px/1 sans-serif;
}
.block_featured {
    background: black;
}
.block_featured .block__title {
    color: white;
    font-size: 30px;
}

Заключение

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

Эта статья была любезно переведена на английский язык Варей Степановой и опубликована во «Frontend Babel» — Generating BEM selectors with CSS preprocessors.