Этот текст устарел. Читайте новую статью вместо него.

Что такое БЭМ

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

БЭМ решает следующие задачи:

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

Разделение ответственности

Разделение кода на логические составляющие (блоки и их элементы) позволяет разделить ответственность. Разные части кода страницы могут писать разные люди.
Например, один разработчик ответственнен за colorpicker (на всём проекте), другой - за компонент валидации форм, третий - за подвал сайта и т.д.

Использование без погружения

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

Повторное использование

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

Мультилингвальность

Единая предметная область

В web-технологиях финальный продукт состоит из разных технологий (например, HTML, CSS, JS). БЭМ-методология декларирует единые термины и подходы к реализации во всех применяемых технологиях.
Напрмер, JavaScript-реализации блоков не оперируют понятиями DOM-элементов, а используют следующий уровень абстрации - БЭМ-дерево.

Технологии реализации

Картинки и документация - это тоже технологии реализации блока.

Оптимизация выполнения production runtime

Использовать только нужное

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

Браузер как assembler

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

Послойная структура

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

Определения

Блок

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

Пример
Блок b-search поисковой формы

Block b-search

Элемент

Часть блока, которая отвечает за какую-то отдельную функцию.
Элементы блока имеют смысл только в рамках своего родителя. Могут быть обязательными и не обязательными.

Пример
Поле ввода и кнопка — элементы блока b-search

Input and button elements of b-search block

Модификатор

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

Пример
Модификатор блока b-search задает ему цвет фона

Background color modificator of b-search block

Уровень переопределения

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

Пример

Blocks levels

Соглашения по именованию

Имя блока

Имя блока формируется как префикс-имя-блока.

Примеры

    b-menu
    b-popup
    i-popup

Префикс блока

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

b- (от block)
Префикс блока с визуальным представлением на странице.

Пример
Попап с тенью имеет конкретное визуальное представление. Может иметь или не иметь хвостик или закрывающий крестик. Может быть разных цветов. Может отображаться в разных направлениях.

b-popup block

i- (от include)
Префикс абстрактного блока, который не существует сам по себе, но используется для построения других блоков.
Или префикс блока, не имеющего визуального представления и реализующего какую-то функциональность.

Пример
Блок i-popup не имеет конкретного визуального представления. Хранит в себе функциональность, на основе которой строится блок b-popup. Без блока i-popup блок b-popup не существует.

i-popup block

Имя элемента

Полное имя элемента формируется так, чтобы из него можно было определить принадлежность данного элемента к конкретному блоку.
Полное имя элемента создается по схеме: b-имя-блока__имя-элмента

Примеры

    b-menu__item
    b-popup__content

Имя модификатора блока

Полное имя модификатора блока формируется так, чтобы из него можно было определить принадлежность данного модификатора к конкретному блоку. Полное имя модификатора блока создается по схеме: b-имя-блока_имя-модификатора_значение-модификатора.

Примеры

    b-menu_layout_horiz
    b-menu_layout_vert
    b-popup_direction_up

Имя модификатора элемента

Полное имя модификатора элемента формируется так, чтобы из него можно было определить принадлежность данного модификатора к конкретному элементу конкретного блока. Полное имя модификатора элемента создается по схеме: b-имя-блока__имя-элемента_имя-модификатора_значение-модификатора.

Примеры

    b-menu__item_state_current
    b-popupa__content_visibile_yes
    b-popupa__content_visibile_no

Варианты модификации блока

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

Блоку/элементу добавляется модификатор и изменение блока/элемента описывается в коде этого модификатора.

Примеры

По умолчанию фон у попапа осутствует.

Transparent b-popup

Добавление модификатора theme блоку b-popup добавляет ему фон.
theme=yellow добавляет фон желтого цвета:

Yellow background modificator for b-popup block

theme=black добавляет фон черного цвета:

Black background modificator for b-popup block

У блока может быть одновременно несколько модификаторов.

Примеры

Модификатор theme — отвечает за фон попапа. Модификатор direction — отвечает за направление отображения попапа.

theme=yellow и direction=left — попап желтого цвета открывается влево:

Left direction modificator for b-popup block

theme=yellow и direction=right — попап желтого цвета открывается вправо:

Right direction modificator for b-poup block

Модификатор блока может изменять структуру блока (например, добавлять/удалять элементы) или его реализацию.

Пример
Модификатор has-close=yes — добавляет элемент «закрывающий крестик». Добавляет отступы блоку, освобождая место крестика.

Modificator for adding close icon to b-popup

Контекстом

Блок при размещении в другом блоке может менять свой внешний вид или поведение.

Пример
Цвет текста вложенного блока меняется с зеленого на красный, если он помещен в блок с синим фоном с красными буквами.

Примеры

Переключатель языков (b-lang-switcher)

Language switcher block

Переключатель языков (b-lang-switcher) в подвале страницы (b-foot), уменьшается размер шрифта.

b-lang-switcher with small text

Уровнем переопределения

Изменения создаются на следующем уровне переопределения и добавляют или изменяют функциональность блока.

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

Модификация уровнем переопределения используется, например, в тестовом проекте bem-bl-test, где в папке blocks содержится код, доопределяющий поведение блока b-link. Это позволяет изменить основную реализацию блока (из библиотеки bem-bl) под нужды проекта.

Структура блока на файловой системе

Реализация блока состоит из набора технологий, к примеру:

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

В файловой системе реализации в разных технологиях записаны в соответствующие файлы. Имена файлов соответствуют соглашению об именовании.

Каждый блок в директории, элементы и модификаторы в отдельных директорияx

    blocks/
        b-menu/
            _layout/
                b-menu_layout_horiz.css
                b-menu_layout_horiz.bemhtml
                b-menu_layout_vertical.css
                b-menu_layout_vertical.bemhtml
            __elem/
                b-menu__elem.css
                b-menu__elem.bemhtml
            b-menu.css
            b-menu.js
            b-menu.bemhtml

Каждый блок в директории, элементы и модификаторы в отдельных файлах без директорий

    blocks/
        b-menu/
            b-menu_layout_horiz.css
            b-menu_layout_horiz.bemhtml
            b-menu_layout_vertical.css
            b-menu_layout_vertical.bemhtml
            b-menu__elem.css
            b-menu__elem.bemhtml
            b-menu.css
            b-menu.js
            b-menu.bemhtml

Каждый блок в директории, элементы и модификаторы в файлах блока

    blocks/
        b-menu/
            b-menu.css
            b-menu.js
            b-menu.bemhtml

Директории не используются, элементы и модификаторы в отдельных файлах

    blocks/
        b-menu_layout_horiz.css
        b-menu_layout_horiz.bemhtml
        b-menu_layout_vertical.css
        b-menu_layout_vertical.bemhtml
        b-menu__elem.css
        b-menu__elem.bemhtml
        b-menu.css
        b-menu.js
        b-menu.bemhtml

Директории не используются, элементы и модификаторы в файлах блока

    blocks/
        b-menu.css
        b-menu.js
        b-menu.bemhtml

Технологии реализации

БЭМ-метод подразумевает реализацию блока в различных технологиях.

Level can include different techs

HTML

Реализация возможна в нескольких вариантах.

http:clubs.ya.ru/bem/replies.xml?item_no=712

Тэги HTML и классы

В HTML-е каждая БЭМ-сущность определяется тегом и своим классом или классами.

    <div class="block-name">
        <i class="block-name__elem"></i>
        ...
    </div>
    <div class="block-name block-name_modifier">...</div>

Кастомные тэги

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

    <copyright>
        <copyright-elem>...</copyright-elem>
        ...
    </copyright>
    <copyright class="block-name_modifier">...</copyright>

CSS

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

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

Важно: CSS-свойства не описываются через id, а именно вот так *НЕ ПИШЕМ*:

    #header {}

CSS-классы для БЭМ-сущностей соответствуют полным именам этих сущностей
См. [соглашение об именовании](#naming)

Примеры именования CSS-классов

 .b-popup {}                 // CSS-класс блока
 .b-popup__content {}        // CSS-класс элемента блока
 .b-popupa_theme_yellow {}   // CSS-класс модификатора блока

По возможности отказываемся от Селекторов типа. Селекторы типа — селекторы, которые применяются к DOM-узлам без CSS-класса (элементо-зависимые селекторы).

Например, вот так НЕ ПИШЕМ:

    p
    {
    	color: #ccc;
    }

    table.news td
    {
    	border-bottom: 1px solid #ccc;
    }

    .list li
    {
    	float: left;
    }

Отказ от использования Селекторов типа и CSS-каскада уменьшает Reflow Time — чистое время наложения стилей на сформированный DOM.
Ссылка на запись с более подробным описанием

JavaScript

В JS мы также работаем в терминах "Блок-Элемент-Модификатор". DOM-представление блоков рассматривается как более низкий уровень реализации.
Допустимы блоки без DOM-представления. Обычно это блоки-хелперы.

Пример блоков без DOM-представления:

    i-request                // Конструктор запросов
    i-request_type_ajax      //Конструктор AJAX-запросов

Клиентским JS-фреймворком для работы со страницей в терминах БЭМ является JS-реализация блока i-bem библиотеки bem-bl:

Шаблоны

Одной из реализаций блока может быть его шаблон, который по входным данным строит HTML.

В бибилиотеке bem-bl в качестве шаблонной технологии используется bemhtml. Это надмножество JS-шаблонизатора xjst, написанные на нём шаблоны блоков компилируются в JavaScript и могут быть выполнены как на сервере (через Node.js), так и на клиенте (браузере).

Представление в DOM

В простейшем случае блок соответствует DOM-узлу, один к одному. Но важно понимать, что DOM-узел и блок — это не всегда одно и тоже.

Микс

Под миксом подразумевается смешивание на одном DOM-узле разных блоков и элементов.

На одном DOM-узле может быть:

Пример

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

Вертикальное расположение элементов

Tabbed pane with vertical tabs
    <div class="b-tabbed-pane b-tabbed-pane__tabs">
        ...
    </div>
    <div class="b-tabbed-pane b-tabbed-pane__panels">
        ...
    </div>

Горизонтальное расположение элементов

Tabbed pane with horizaontal tabs
    <table class="b-layout-table">
    <tr>
        <td class="b-layout-table__column b-layout-table__column_side_left">
            <div class="b-tabbed-pane b-tabbed-pane__tabs">
                ...
            </div>
        </td>
        <td class="b-layout-table__column b-layout-table__column_side_right">
            <div class="b-tabbed-pane b-tabbed-pane__panels">
                ...
            </div>
        </td>
    </tr>
    </table>

Структура БЭМ-проекта на файловой системе

Проект, реализованный на БЭМ, состоит из одного или нескольких уровней переопределения и кода страниц.

Приведенные ниже примеры структуры не жесткие и могут быть изменены в зависимости от потребностей проекта.

Страница

Совокупность файлов, которые определяют внешний вид и поведение конкретной страницы.

Пример

    pages/
        index/
            index.css
            index.js
            index.bemhtml
            index.bemdecl.js
            index.bemjson.js

Страница состоит из описания блоков, которые на ней используются (.bemdecl), и конкретного их расположения (.bemjson). Это исходный код страницы. Из него генерируются все необходимые для отображения файлы (.css, .js, etc)

Исходный код страницы

index.bemdecl.js

На странице b-page используем b-menu и b-link

Пример

    exports.blocks = [
        {
            "name": "b-page"
        },
        {
            "name": "b-menu",
            "mods": [
                {
                    "name": "layout",
                    "vals": [
                        "horiz"
                    ]
                },
                {
                    "name": "theme",
                    "vals": [
                        "red"
                    ]
                }
            ],
            "elems": [
                {
                    "name": "item",
                    "mods": [
                        {
                            "name": "state",
                            "vals": [
                                "current"
                            ]
                        }
                    ]
                }
            ]
        },
        {
            "name": "b-link"
        }
    ]
index.bemjson.js

На странице b-page используем b-menu и b-link

Пример

    ({
        block: 'b-page',
        content: {
            block: 'b-menu',
            mods: { layout: 'horiz', theme: 'red' },
            content: [
                {
                    elem: 'item',
                    elemMods: { state: 'current' },
                    content: 'Главная'
                },
                {
                    elem: 'item',
                    content: {
                        block: 'b-link',
                        url: '/',
                        content: 'Находки'
                    }
                },
                {
                    elem: 'item',
                    content: {
                        block: 'b-link',
                        url: '/',
                        content: 'Поиск'
                    }
                },
                {
                    elem: 'item',
                    content: {
                        block: 'b-link',
                        url: '/',
                        content: 'О проекте'
                    }
                }
            ]
        }
    })

Генерируемые файлы

index.css

На странице b-page используем b-menu и b-link

Пример

    @import url(../../bem-bl/blocks-desktop/b-menu/b-menu.css);
    @import url(../../bem-bl/blocks-desktop/b-menu/item/b-menu__item.css);
    @import url(../../bem-bl/blocks-desktop/b-menu/item/_state/b-menu__item_state_current.css);
    @import url(../../bem-bl/blocks-desktop/blocks/b-menu/_layout/b-menu_layout_horiz.css);
    @import url(../../bem-bl/blocks-desktop/b-link/b-link.css);
    @import url(blocks/b-menu/_theme/b-menu_theme_red.css);
index.js

На странице b-page используем b-menu и b-link

    include("../../bem-bl/blocks-desktop/i-jquery/__inherit/i-jquery__inherit.js");
    include("../../bem-bl/blocks-desktop/i-jquery/__identify/i-jquery__identify.js");
    include("../../bem-bl/blocks-desktop/i-jquery/__is-empty-object/i-jquery__is-empty-object.js");
    include("../../bem-bl/blocks-desktopery/__debounce/i-jquery__debounce.js");
    include("../../bem-bl/blocks-desktop/i-jquery/__observable/i-jquery__observable.js");
    include("../../bem-bl/blocks-desktop/i-bem/i-bem.js");
    include("../../bem-bl/blocks-desktop/i-bem/__internal/i-bem__internal.js");
    include("../../bem-bl/blocks-desktop/i-jquery/__stringify/i-jquery__stringify.js");
    include("../../bem-bl/blocks-desktop/i-bem/html/i-bem__html.js");
    include("../../bem-bl/blocks-desktop/i-jquery/__leftclick/i-jquery__leftclick.js");
    include("../../bem-bl/blocks-desktop/i-bem/__dom/i-bem__dom.js");
    include("../../bem-bl/blocks-desktop/i-bem/__dom/_init/i-bem__dom_init_auto.js");

Уровни переопределения проекта

Проект состоит из уровней переопределения. Количество уровней переопределения зависит от проекта, всегда есть минимум 1 уровень.

Реализация блока может быть на нескольких уровнях. Для каждой страницы можно настроить свое используемое множество уровней. Финальная реализация блока на странице собирается со всех уровней для этой страницы.

Расположение уровней на проектах

Только блоки проекта
    project/
        blocks/
            b-head/
                b-head.css
                b-head.bemhtml
            b-foot/
                b-foot.css
                b-foot.bemhtml
            b-sidebar/
                b-sidebar.css
                b-sidebar.bemhtml
Блоки проекта и отдельных страниц проекта
    project/
        blocks/
            b-head/
                b-head.css
                b-head.bemhtml
            b-foot/
                b-foot.css
                b-foot.bemhtml
            b-page/
                b-page.bemhtml
            b-sidebar/
                b-sidebar.css
                b-sidebar.bemhtml
        pages/
            index/
                blocks/
                    b-head/
                        b-head.css
                        # переопределили вид шапки на заглавной странице
            about/
                blocks/
                    b-about-text/
                    # добавили новый блок
                        b-about-text.css
                        b-about-text.bemhtml
Блоки фреймворка и проекта
    project/
        framework/
            b-menu/
                b-menu.css
                b-menu.bemhtml
            b-page/
                b-page.css
                b-page.bemhtml
        blocks/
            b-head/
                b-head.css
                b-head.bemhtml
            b-foot/
                b-foot.css
                b-foot.bemhtml
            b-page/
            # переопределение шаблонов b-page из уровня framework'а
                b-page.bemhtml
            b-sidebar/
                b-sidebar.css
                b-sidebar.bemhtml
Блоки фреймворка, проекта и страниц
    project/
        framework/
            b-menu/
                b-menu.css
                b-menu.bemhtml
            b-page/
                b-page.css
                b-page.bemhtml
        blocks/
            b-head/
                b-head.css
                b-head.bemhtml
            b-foot/
                b-foot.css
                b-foot.bemhtml
            b-page/
            # переопределение шаблонов b-page из уровня framework'а
                b-page.bemhtml
            b-sidebar/
                b-sidebar.css
                b-sidebar.bemhtml
        pages/
            index/
                blocks/
                    b-head/
                        b-head.css
                        # переопределили вид шапки на заглавной странице
            about/
                blocks/
                    b-about-text/
                    # добавили новый блок
                        b-about-text.css
                        b-about-text.bemhtml
Блоки проекта, страниц и темы
    project/
        blocks/
            b-head/
                b-head.css
                b-head.bemhtml
            b-foot/
                b-foot.css
                b-foot.bemhtml
            b-page/
            # переопределение шаблонов b-page из уровня framework'а
                b-page.bemhtml
            b-sidebar/
                b-sidebar.css
                b-sidebar.bemhtml
        pages/
            index/
                blocks/
                    b-head/
                        b-head.css
                        # переопределили вид шапки на заглавной странице
            about/
                blocks/
                    b-about-text/
                    # добавили новый блок
                        b-about-text.css
                        b-about-text.bemhtml
        themes/
            black/
            # не раскладываем файлы по директориям, кладем их рядом
                b-head.css
                b-foot.css
                b-sidebar.css
            yellow/
                b-head.css
                b-foot.css
                b-sidebar.css

Реализация блока

Реализация блока состоит из набора технологий, к примеру:

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

Пример реализации блока b-domik

Домик на странице
Login house on page
Домик в попапе
Login house in popup
    b-domik/
        # общие файлы реализации для всех типов домика
        b-domik.css
        b-domik.ie.css
        b-domik.js
        b-domik.xsl
        _type/
        # папка с модификаторами для блока
            b-domik_type_onpage.css    # домик, прибитый на странице
            b-domik_type_onpage.ie.css
            b-domik_type_onpage.js
            b-domik_type_onpage.xsl
            b-domik_type_popup.css    # домик как всплывающее окно
            b-domik_type_popup.ie.css
            b-domik_type_popup.js
            b-domik_type_popup.xsl
        __lock/
        # замочек — элемент блока
            b-domik__lock.css
            b-domik__lock.png
            b-domik__lock.xsl
            _visibility/
            # модификатор элемента lock, делает его видимым
                b-domik__lock_visibility_visible.css
        __shadow/
        # тень — элемент блока
            b-domik__shadow.css
            b-domik__shadow.ie.css
            b-domik__shadow.png

Инструменты

Для работы с файлами по БЭМ-методу разрабатываются специальные инструменты bem-tools. Они позволяют создавать файлы БЭМ-сущностей в различных технологиях и сборки для страниц.

Разработка ведется в репозитории bem-tools

Полезные ссылки

Доклады с конференций

Связанные ссылки

Репозитории