Форум

Методология

Инструментарий

Платформа

Сообщество

CSS по БЭМ

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

Основные принципы работы с CSS рассматриваются в следующих разделах:

HTML для CSS

Как сделать HTML-обертку?

Традиционно, HTML-обертки применяются с целью:

  • расположить HTML-элементы относительно других;
  • позиционировать элементы внутри блока.

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

Расположение блока относительно других блоков

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

Пример

HTML-реализация:

<body class="page">
    <!-- верхний колонтитул и навигация -->
    <header class="header page__header">...</header>

    <!-- нижний колонтитул -->
    <footer class="footer page__footer">...</footer>
</body>

CSS-реализация:

.page__header {
    padding: 20px;
}

.page__footer {
    padding: 50px;
}

Позиционирование элементов внутри блока

Для позиционирования вложенных HTML-элементов, в большинстве случаев, создается дополнительный элемент блока (например, block__inner).

Пример

HTML-реализация:

<body class="page">
    <div class="page__inner">
        <!-- верхний колонтитул и навигация -->
        <header class="header">...</header>

        <!-- нижний колонтитул -->
        <footer class="footer">...</footer>
    </div>
</body>

CSS-реализация:

.page__inner {
    margin-right: auto;
    margin-left: auto;
    width: 960px;
}

Селекторы

В БЭМ не используют селекторы тегов и идентификаторов. Стили блоков и элементов описываются через селекторы классов.

Селекторы классов

Позволяют указать конкретный HTML-элемент страницы, независимо от тега. Обращение к селектору класса происходит через атрибут class, который должен иметь каждый HTML-элемент.

Значением атрибута class может быть разделенный пробелами список слов. Это позволяет использовать разные БЭМ-сущности на одном DOM-узле.

Пример

HTML-реализация:

<header class="header">
    <!--
    `header__button` — элемент блока `header`;
    `button` — блок;
    `button_theme_islands` — модификатор.
    -->
    <button class="header__button button button_theme_islands">...</button>
</header>

Совмещение тега и класса в селекторе

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

Пример

HTML-реализация:

<button class="button">...</button>

CSS-правила заданы в селекторе button.button.

Допустим, блоку добавили модификатор active с значением true:

<button class="button button_active">...</button>

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

В результате развития проекта могут, например, появиться блоки с селекторами input.button, span.button и a.button. В таком случае все модификаторы блока button и вложенные в него элементы потребуют четыре разные декларации для каждого случая.

Нужно стараться использовать простые селекторы классов:

.button {}
.button_active {}

Вложенные селекторы

Методология БЭМ допускает использование вложенных селекторов, но рекомендует свести их к минимуму. Вложенные селекторы увеличивают связность кода и делают его повторное использование невозможным.

Допустимые случаи использования

Вложенность уместна, если нужно изменить стили элементов в зависимости от состояния блока или заданной темы.

Пример

CSS-реализация:

.button_hovered .button__text
{
    text-decoration: underline;
}

.button_theme_islands .button__text
{
    line-height: 1.5;
}

Комбинированные селекторы

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

Пример

HTML-реализация:

<button class="button button_theme_islands">...</button>

CSS-правила заданы в селекторе .button.button_theme_islands.

Допустим, блоку добавили модификатор active с значением true:

<button class="button button_theme_islands button_active">...</button>

Селектор .button_active не переопределит свойства блока, записанные как .button.button_theme_islands, так как специфичность .button.button_theme_islands выше, чем у .button_active. Для успешного переопределения селектор модификатора блока также должен быть скомбинирован с селектором .button и объявлен ниже .button.button_theme_islands, так как специфичность обоих селекторов одинакова:

CSS-реализация:

.button.button_theme_islands {}
.button.button_active {}

Нужно стараться использовать простые селекторы классов:

.button_active {}
.button {}

Именование

Имя селектора должно полно и точно описывать представляемую им БЭМ-сущность.

В качестве примера рассмотрим следующие четыре строки CSS-кода:

.button {}
.button__icon {}
.button__text {}
.button_theme_islands {}

С определенной долей вероятности можно предположить, что мы имеем дело с одним блоком и его HTML-реализация выглядит следующим образом:

<button class="button button_theme_islands">
    <span class="button__icon"></span>

    <span class="button__text">...</span>
</button>

Сложнее сделать подобное предположение с такой группой селекторов:

.button {}
.icon {}
.text {}
.theme_islands {}

Имена icon, text, theme_islands не так информативны.

Общие правила именования блоков, элементов и модификаторов позволяют:

  • сделать имена CSS-селекторов максимально информативными и понятными;
  • решить проблему коллизии имен;
  • независимо описывать стили блоков и их опциональных элементов.

Пример

HTML-реализация:

<!-- Блок `logo` -->
<div class="logo logo_theme_islands">
    <img src="URL" alt="logo" class="logo__img">
</div>

<!-- Блок `user` -->
<div class="user user_theme_islands">
    <img src="URL" alt="user-logo" class="user__img">
    ...
</div>

Именование CSS-классов:

.logo {}                  /* CSS-класс блока `logo` */

.logo__img {}             /* CSS-класс элемента `logo__img` */

.logo_theme_islands {}    /* CSS-класс модификатора `logo_theme_islands` */

.user {}                  /* CSS-класс блока `user` */

.user__img {}             /* CSS-класс элемента `user__img` */

.user_theme_islands {}    /* CSS-класс модификатора `user_theme_islands` */

Модификаторы

Модификаторами в БЭМ задают блокам внешний вид, состояние и поведение. Изменение оформления блока производится при помощи установки/снятия модификатора.

Пример

HTML-реализация:

<button class="button button_size_s">...</button>

CSS-реализация:

.button {
    font-family: Arial, sans-serif;
    text-align: center;
}

.button_size_s {
    font-size: 13px;
    line-height: 24px;
}

.button_size_m {
    font-size: 15px;
    line-height: 28px;
}

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

Например, так:

<button class="button button_size_s">...</button>
<button class="button button_size_m">...</button>

Это избавляет от ненужного «Copy-Paste».

Миксы

Позволяют:

  • совмещать поведение и стили нескольких сущностей без дублирования кода;
  • одинаково форматировать разные HTML-элементы.

Внешняя геометрия и позиционирование

В CSS по БЭМ стили, отвечающие за внешнюю геометрию и позиционирование, задаются через родительский блок.

Пример

HTML-реализация:

<!-- Блок `header` -->
<header class="header">
    <button class="button header__button">...</button>
</header>

CSS-реализация кнопки:

.button {
    font-family: Arial, sans-serif;
    text-align: center;
    border: 1px solid black;    /* Рамка */
}
.header__button {
    margin: 30px;               /* Отступ */
    position: relative;
}

В данном примере внешняя геометрия и позиционирование блока button задана через элемент header__button. Блок button не специфицирует никакие отступы и может быть легко переиспользован в любом месте.

HTML-реализация:

<!-- Блок `footer` -->
<footer class="footer">
    <button class="button">...</button>
</footer>

Стилизация групп блоков

Иногда необходимо применить одинаковое форматирование сразу к нескольким различным HTML-элементам веб-страницы. Обычно для решения подобных задач применяют групповые селекторы.

Пример

HTML-реализация:

<article class="article">...</article>

<footer class="footer">
    <div class="copyright">...</div>
</footer>

CSS-реализация:

article, .footer div {
    font-family: Arial, sans-serif;
    font-size: 14px;
    color: #000;
}

В данном примере текст внутри блоков article и copyright имеет один и тот же цвет и шрифт.

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

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

Пример

HTML-реализация:

<article class="article text">...</article>

<footer class="footer">
    <div class="copyright text">...</div>
</footer>

CSS-реализация:

.text {
    font-family: Arial, sans-serif;
    font-size: 14px;
    color: #000;
}

Разделение кода на части

К CSS по БЭМ применяются основные принципы организации и хранения кода:

  • разделение кода на отдельные части — логика работы каждого блока, его опциональных элементов и модификаторов описывается в отдельных файлах;
  • CSS-файлы для каждого компонента хранятся в соответствии с правилами организации файловой структуры БЭМ-проекта.

Разделение кода на части и строгая организация файловой структуры проекта позволяет:

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

Пример

Блок button в файловой структуре проекта:

button/                      # Директория блока button
    _size
        button_size_s.css    # Реализация модификатора в технологии CSS

    button.css               # Реализация блока button в технологии CSS

Такое разделение позволяет быстро находить нужные файлы.

Принцип единственной ответственности

Как и в объектно-ориентированном программировании, принцип единственной ответственности (англ. Single responsibility principle) в CSS по БЭМ означает, что каждая CSS-реализация должна иметь одну ответственность.

Пример

HTML-реализация:

<header class="header">
    <button class="button header__button">...</button>
</header>

CSS-реализация:

.button {
    font-family: Arial, sans-serif;
    border: 1px solid black;
    background: #fff;
}

Ответственность: внешняя геометрия и позиционирование (зададим внешнюю геометрию и позиционирование для блока button через элемент header__button).

Верно:

.header__button {
    margin: 30px;
    position: relative;
}

Неверно:

.header__button {
    font-family: Arial, sans-serif;
    position: relative;
    border: 1px solid black;
    margin: 30px;
}

Селекторы с одиночной ответственностью придают коду больше гибкости.

Принцип открытости/закрытости

Любой HTML-элемент страницы должен быть открыт для модификации, но закрыт для изменения. Разрабатывать новые CSS-реализации следует так, чтобы не пришлось менять уже существующие.

Пример

HTML-реализация:

<button class="button">...</button>
<button class="button">...</button>

CSS-реализация:

.button {
    font-family: Arial, sans-serif;
    text-align: center;
    font-size: 11px;
    line-height: 20px;
}

Предположим, что появилась необходимость изменить размер одной из кнопок. Следуя принципу открытости/закрытости, модифицируем кнопку.

HTML-реализация:

<button class="button">...</button>
<button class="button button_size_s">...</button>

CSS-реализация:

.button {
    font-family: Arial, sans-serif;
    text-align: center;
    font-size: 11px;
    line-height: 20px;
}

.button_size_s {
    font-size: 13px;
    line-height: 24px;
}

Существующая функциональность кнопки расширена при помощи класса button_size_s (переопределены свойства font-size и line-height). Теперь на странице есть две кнопки разного размера.

Нарушение принципа открытости/закрытости

  • Изменение существующей CSS-реализации

    .button {
        font-family: Arial, sans-serif;
        text-align: center;
        font-size: 13px;
        line-height: 24px;
    }
    
    

    Текущая CSS-реализация кнопки должна быть закрыта для редактирования. Изменения коснутся всех блоков button.

  • Модификация контекстом

    .button {
        font-family: Arial, sans-serif;
        text-align: center;
        font-size: 11px;
        line-height: 20px;
    }
    
    .content .button {
        font-size: 13px;
        line-height: 24px;
    }
    

Оформление кнопки стало зависеть от ее расположения. Изменения коснутся всех блоков button внутри блока content.

DRY

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

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

Пример

HTML-реализация:

<button class="button">...</button>
<button class="btn">...</button>

CSS-реализация:

.button {
    font-family: Arial, sans-serif;
    text-align: center;
    color: #000;
    background: #fff;
}

.btn {
    font-family: Arial, sans-serif;
    text-align: center;
    color: #000;
    background: rgba(255, 0, 0, 0.4);
}

Как видно из примера, в селекторе btn повторена существующая реализация блока button.

Перепишем пример в соответствии с принципом DRY:

HTML-реализация:

<button class="button button_theme_islands">...</button>
<button class="button button_theme_simple">...</button>

CSS-реализация:

.button {
    font-family: Arial, sans-serif;
    text-align: center;
}

.button_theme_islands {
    color: #000;
    background: #fff;
}

.button_theme_simple {
    color: #000;
    background: rgba(255, 0, 0, 0.4);
}

Благодаря добавлению модификаторов, мы избавились от блока btn.

Важно! Принцип DRY имеет отношение только к функционально однотипным компонентам страницы, например, кнопки.

Пример

buttons

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

Не имеет смысла объединять разные по типу блоки, только потому что у них, например, одинаковый цвет или размер.

Пример

yellow-blocks

Композиция вместо наследования

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

Новые CSS-реализации в БЭМ собирают из уже существующих, путем их объединения. Это сохраняет код несвязным и гибким.

Пример

Допустим, есть три готовые реализации:

  • кнопка — блок button;
  • меню — блок menu;
  • всплывающее окно — блок popup.

Задача

Реализовать раскрывающийся список (блок select).

Разработать раскрывающийся список с произвольным внешним видом — весьма непростая задача. Однако, если есть готовые компоненты (кнопка, всплывающее окно и меню), остается только правильно описать их взаимодействие.

Пример

HTML-реализация:

<div class="select">
    <button class="button select__button">
        <span class="button__text">Блок</span>
    </button>
</div>

<div class="popup">
    <div class="menu">
        <div class="menu__item">Блок</div>
        <div class="menu__item">Элемент</div>
        <div class="menu__item">Модификатор</div>
    </div>
</div>

Работа с уровнями переопределения

Применение принципов БЭМ-методологии к CSS позволяет разделять представление блоков по разным уровням.

Разделение по уровням позволяет:

  • реализовывать новый внешний вид блока на другом уровне переопределения, сохраняя предыдущий, наследовать и дополнять его;
  • полностью перекрывать внешний вид блока (переопределять);
  • добавлять блоки с новым представлением.

С помощью уровней переопределения можно создать универсальную CSS-библиотеку блоков и изменять ее на проектном уровне. Затем использовать сборку и включать в проект только необходимое представление блоков.

Пример

common.blocks/
    button/
        button.css    # Базовая CSS-реализация кнопки

desktop.blocks/
    button/
        button.css    # Особенности кнопки для desktop

mobile.blocks/
    button/
        button.css    # Особенности кнопки для mobile

При сборке в файл desktop.css попадут все базовые CSS-правила кнопки с уровня common и переопределенные правила с уровня desktop.

@import "common.blocks/button/button.css";    /* Базовые CSS-правила */
@import "desktop.blocks/button/button.css";   /* Особенности desktop */

Файл mobile.css будет включать базовые CSS-правила кнопки с уровня common и переопределенные правила с уровня mobile.

@import "common.blocks/button/button.css";    /* Базовые CSS-правила */
@import "mobile.blocks/button/button.css";    /* Особенности mobile */

Разделение представления блока button по разным уровням позволяет:

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

    common.blocks/button/button.css

    .button {
        font-family: Arial, sans-serif;
        font-size: 11px;
        line-height: 24px;
        background: #fff;
    }
    
    

    desktop.blocks/button/button.css

    .button {
        font-family: 'Roboto', sans-serif;
        font-size: 13px;
        line-height: 28px;
        background: yellow;
    }
    
    
  • Добавить или частично изменить внешний вид блока на другом уровне переопределения.

    common.blocks/button/button.css

    .button {
        font-family: Arial, sans-serif;
        font-size: 11px;
        line-height: 24px;
        background: #fff;
    }
    
    

    desktop.blocks/button/button.css

    .button {
        background: #fff;
        color: rgb(255, 0, 0);
        box-shadow: 0 0 10px rgba(0,0,0,0.5);
    }
    

Как перейти на CSS по БЭМ

Чтобы реализовать принципы БЭМ в проекте, необходимо:

  • абстрагироваться от DOM-модели и научиться создавать блоки;
  • не использовать ID-селекторы и селекторы тегов;
  • минимизировать количество вложенных селекторов;
  • использовать соглашение по именованию CSS-классов, чтобы избежать коллизии имен и сделать имена селекторов максимально информативными и понятными;
  • работать в единых терминах блоков, элементов и модификаторов;
  • выносить в модификаторы CSS-свойства блока, изменение которых кажется вероятным;
  • использовать миксы;
  • разделять код на мелкие независимые части для удобства работы с отдельными блоками;
  • повторно использовать блоки.

Как начать реализовывать идеи БЭМ в существующем проекте

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

После знакомства с CSS по БЭМ переходите к рассмотрению особенностей реализации JavaScript по БЭМ-методологии.

Если вы заметили ошибку или хотите чем-то дополнить статью, вы всегда можете или написать нам об этом на Гитхабе, или поправить статью с помощью prose.io.