# История создания БЭМ
Однажды, в далекой-далекой стране, компания [Яндекс](https://company.yandex.ru/) начала разрабатывать поиск по Интернету и сопутствующие сервисы.
Время шло, сервисы развивались и всё больше разработчиков интерфейсов вкладывали свои усилия в развитие Яндекса.

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

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

<img src="https://img-fotki.yandex.ru/get/3607/246231603.0/0_14ae54_3b59c928_orig.png" alt="RIF Voronezh" style="float:right;padding:0 0 20px 20px;width:200px;height:auto;">

Для начала, давайте отмотаем время назад, в 2005 год, и заглянем через плечо ужасно занятого разработчика Яндекса...

## …как всё начиналось
В 2005 году фокус был в основном на серверной части сервисов. С точки зрения интерфейса, типичный проект Яндекса был набором статических HTML-страниц, которые использовались как основа для создания шаблонов на XSL.

Эти HTML-странички хранились в отдельной директории примерно в такой структуре:

```
about.html
index.html
…
project.css
project.js
i/
  yandex.png
```

Для каждой страницы создавался отдельный HTML-файл. Стили для них складывались в один файл на весь проект — `project.css`, скрипты — в `project.js`.

В то время JavaScript использовался как вспомогательный инструмент для оживления страницы и всё помещалось в небольшой файл.

Картинки складывались в отдельную директорию, их было много. В те времена еще требовалась поддержка IE 5 и не было поддержки CSS3 в браузерах, картинки использовались для реализации любого оформления - даже для закругленных уголков. :-)

Все стили находились в одном файле, и для отделения стилей разных частей страницы использовались комментарии с указанием начала и конца:

```css
/* Content container (begin) */
    #body
    {
        font: 0.8em Arial, sans-serif;

        margin: 0.5em 1.95% 0.5em 2%;
    }
/* Content container (end) */

/* Graphical banner (begin) */
    .banner
    {
        text-align: center;
    }

    .banner a
    {
        text-decoration: none;
    }
/* Graphical banner (end) */
```

В верстке использовались как `id`, так и `классы`.

Сверстанные статические HTML-странички нарезались в XSL-шаблоны, которые использовались в продакшне. Если HTML изменялся, все правки вручную переносились в XSL. И наоборот: изменения в шаблонах несли за собой изменения в HTML - для поддержания статического HTML в актуальном состоянии.

Это было сложно, и даже когда это не было сложно — это было тупо.

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

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

Типичный код того времени:

```css
/* Albums (begin) */
    .result .albums .info
    {
        padding-right: 8.5em;
    }

    .result .albums .title
    {
        float: left;
        padding-bottom: 0.3em;
    }

    .result .albums .album .listen
    {
        float: left;
        padding: 0.3em 1em 0 1em;
    }

    .result .albums .album .buy
    {
        float: left;
        padding: 0.4em 1em 0 1.6em;
    }

    .result .albums .info i
    {
        font-size: 85%;
    }
/* Albums (end) */
```

Здесь используется длинный каскад.

Еще пример:

```css
/* Картинки на фоне (begin) */
    #foot div
    {
        height: 71px;
        background: transparent url(../i/foot-1.png) 4% 50% no-repeat;
    }

    #foot div div
    {
        background-position: 21%;
        background-image: url(../i/foot-2.png);
    }

    #foot div div div
    {
        background-position: 38%;
        background-image: url(../i/foot-3.png);
    }

    #foot div div div div
    {
        background-position: 54%;
        background-image: url(../i/foot-4.png);
    }

    #foot div div div div div
    {
        background-position: 71%;
        background-image: url(../i/foot-5.png);
    }

    #foot div div div div div div
    {
        background-position: 87%;
        background-image: url(../i/foot-6.png);
    }
/* Картинки на фоне (end) */
```

Здесь используются `id` и `теги`.

Одновременно с Музыкой началась верстка Я.ру.

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

### Появление блоков
Нам необходимо было определить предметную область для управления объектами интерфейса страницы. Это был вопрос методологии – нужно было внести больше ясности в то, как мы работаем с такими понятиями, как `класс`, `тег`, `визуальный компонент` и др.

Раньше для создания типичных страниц всё основное время и усилия разработчиков Яндекса тратилось на создание HTML-структуры страницы и написание CSS-стилей для нее. JavaScript воспринимался лишь как сопутствующая технология.

Чтобы облегчить поддержку HTML/CSS различных компонентов, был введен новый термин – `block`.

`Блок` – это часть дизайна страницы или раскладки со своим специфическим и уникальным значением, определенным семантически или визуально.

В большинстве случаев любой элемент на странице (сложный или простой) можно рассматривать как блок, HTML-контейнер которого получает уникальный CSS-класс с тем же именем, что и у блока.

Классы блоков получили префиксы (`b-`, `c-`, `g-`), чтобы отличать их от внутренних классов.

Используемые префиксы, с которых мы начинали:
 * **b-** block
   Независимый блок, может использоваться в любом месте страницы.
 * **с-** control
   Контрол (независимый блок), с которым ассоциирован JavaScript-объект, обеспечивающий его
   функциональность. Может использоваться в любом месте страницы.
 * **g-** global
   Глобальное определение, используется по необходимости. Количество сведено к минимуму.

Кроме префиксов были постфиксы:
 * **-nojs** no javascript
   Стиль применяется в отсутствие JavaScript. Если JavaScript включен, то при загрузке страницы
   вызывается метод `init()` в onload для удаления этого постфикса из всех классов на странице,
   что «включает» JavaScript для этих блоков.

### Появление элементов
### Что внутри?
В HTML-контейнере, формирующем блок, некоторые узлы получают четкое имя CSS-класса. Это не только облегчает создание стилистических правил, независящих от имени тега, но и присваивает семантически значимую роль каждому узлу. Такие внутренние узлы называются `элементами блока`, или просто `элементами`.

Ключевое различие между блоком и элементом заключается в том, что элемент не может существовать вне контекста родительского блока. Так же как и из блока не может быть извлечен ни один элемент. Если элемент становится способным существовать вне блока, его необходимо рассматривать как блок.

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

Элементы, у которых много кода, выделялись комментариями.

```css
/* Head (begin) */
    .b-head { … }

    /* Logo (begin) */
        .b-head .logo { … }
        .b-head .logo a { … }
    /* Logo (end) */

    /* Right side (begin) */
    .b-head .right { … }

        /* Info (begin) */
            .b-head .info { … }
            .b-head .info .exit a { … }
        /* Info (end) */

        /* Search (begin) */
            .b-head .search { … }
            .b-head .search div div, .b-head .search div div i { … }
        /* Search (end) */
    /* Right side (end) */
/* Head (end) */
```

### Структура верстки проекта
В Яндексе разработчики интерфейсов обычно поддерживают несколько проектов одновременно.

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

Степень детализации – это еще одно требование, которое обеспечивает гибкость контроля версий и помогает избежать конфликтов во время ведения параллельной разработки.

Это привело нас к унификации структуры репозитория разных проектов.

CSS, JavaScript и картинки мы начали складывать в отдельные директории.

JavaScript применялся всё чаще, начали подключаться дополнительные компоненты и библиотеки.

Типичная структура верстки проекта:

```
index.html
css/
  yaru.css
  yaru-ie.css
js/
  yaru.js
i/
  yandex.png
```

Валидные хаки для IE писали в основном CSS-файле `yaru.css`.

```css
    /* Common definitions (begin) */
        body
        {
            font: 0.8em Arial, sans-serif;

            padding: 0 0 2em 0;
            background: #fff;
        }

        * html body
        {
            font-size: 80%;
        }
```

Невалидные — в файле `yaru-ie.css`.

```css
    /* Common blocks (begin) */
        /* Artist (begin) */
            .b-artist .i i
            {
                top: expression(7 + (90 - this.parentNode.getElementsByTagName('img')[0].height)/2);
                filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='../i/sticker-lt.png', sizingMethod='crop');
            }
```

## Зачатки общепортального фреймворка
При верстке нескольких проектов с похожим дизайном обязательно существуют общие блоки.

Портал Яндекса содержит сейчас больше 100 разных сервисов, выполненных в одном стиле, а для такого объема данных бездумный copy/paste с проекта
на проект уже не работает.

Так появилось общее хранилище повторно используемых компонентов, которое называлось `общая библиотека
блоков` или просто `Common`.

Первые блоки, которые были туда вынесены: шапка, подвал и стили для статического текста.

Соответствующие файлы хранились на выделенном внутреннем сервере разработчиков (**common.cloudkill.yandex.ru** в примере ниже).

Это было началом работы нашего общепортального фреймворка.

Cтили из него подключались в основной проектный файл при помощи импортов непосредственно с сервера:

```css
@import url(http://common.cloudkill.yandex.ru/css/global.css);
@import url(http://common.cloudkill.yandex.ru/css/head/common.css);
@import url(http://common.cloudkill.yandex.ru/css/static-text.css);
@import url(http://common.cloudkill.yandex.ru/css/foot/common-absolute.css);
@import url(http://common.cloudkill.yandex.ru/css/foot/common-absolute-4-columns.css);
@import url(http://common.cloudkill.yandex.ru/css/list/hlist.css);
@import url(http://common.cloudkill.yandex.ru/css/list/hlist-middot.css);
@import url(http://common.cloudkill.yandex.ru/css/dropdown/dropdown.css);
@import url(http://common.cloudkill.yandex.ru/css/dropdown/dropdown-arrow.css);
@import url(slider.css);
/* Header (begin) */
    /* Service (begin) */
        .b-head .service h1 { … }
        .b-head .service h1, .b-head .service h1 a, .b-head .service h1 b { … }
```

Слишком много импортов - страница загружается медленно. Было принято решение прекомпилировать стили (и позже JS-файлы)
перед выкладкой в продакшн.

Компиляция заменяет `@import` на содержимое внешних файлов (это называется `inlining`) и оптимизирует код:
например, убирает ненужные браузеру пробелы и комментарии.

Наш внутренний инструмент вырос из простого Perl-скрипта в отдельный open-source проект [borschik](https://ru.bem.info/tools/optimizers/borschik/).
Опробуйте его в своем проекте!

## Верстка независимыми блоками
К осени 2007 года правила верстки уже устоялись, и о них захотелось рассказать вне Яндекса.

На ClientSide'07 был сделан доклад про
[верстку независимыми блоками](http://vitaly.harisov.name/article/independent-blocks.html),
которая на тот момент составляла основу наших HTML-страниц.

В докладе вводится понятие `блок`:

<blockquote>Блоком будем называть фрагмент страницы, который описывается своей разметкой и стилями.</blockquote>

Более позднее [описание в клубе БЭМ](http://clubs.ya.ru/bem/replies.xml?item_no=42).

### Правила блоков
Формулируются правила независимости блока:
  1. Для описания элемента используется `class`, но не `id`;
  2. Каждый блок имеет префикс;
  3. В таблице стилей нет классов вне блоков.

Отказ от `id` дает возможность отображать на странице один и тот же блок несколько раз, а также позволяет использовать на одном DOM-узле несколько классов, что нам пригодилось в дальнейшем.

#### Простые и составные блоки
Блоки делятся на простые и составные.

<blockquote>В простые блоки нельзя вкладывать другие блоки, в составные — можно.</blockquote>

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

Такая классификация фактически мешала нам во многих случаях, и в итоге мы пришли к противоположному принципу:

<blockquote>Любой блок должен позволять вкладывать в него другой блок, когда это возможно.</blockquote>

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

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

Намучавшись с подобными ошибками, мы сформулировали правила более строгой независимости блоков,
которые назвали [абсолютно-независимыми блоками](http://clubs.ya.ru/bem/replies.xml?item_no=43) (АНБ):

  1. Никогда не опираться на элементы - только на классы.
    `.b-user b -> .b-user .first-letter`
  2. Всем классам внутри блока давать имена, начинающиеся с имени этого блока.
    `.b-user .first-letter -> .b-user-first_letter`

Наличие класса у каждого DOM-узла существенно увеличивает объем HTML-кода.

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

#### Префиксы
Как все знают, одна из самых сложных проблем в программировании — подбор имен переменным. :-)

Подробно изучив проблему, мы решили, что будем задавать для блоков разные префиксы, каждый со своей семантикой:

  * **b-** — обычные блоки;
  * **h-** — обертки для нескольких блоков;
  * **l-** — раскладки;
  * **g-** — глобальные стили.

#### Модификация
Модификация может быть определена как особое состояние блока или как метка, несущая определенное свойство.

К примеру, блок `Кнопка` может иметь три состояния: маленькая, нормальная и большая.

Вместо того, чтобы создавать три разных блока, делается модификация одного. Она состоит из имени (например, `size`)
и значения (`small`, `normal` или `big`).

Существуют два варианта модификации:
  1. Блок может изменить свой внешний вид в зависимости от того, где он находится.
     Это модификация от контекста.
  2. Можно добавить блоку второй класс. Это модификация постфиксом, она не зависит от контекста.
     `class="b-block b-block-postfix"`

## Общепортальный фреймворк
Весной 2008 года была поставлена задача создать брендбук, описывающий наш портальный стиль.

Обычное описание подобных технологий устаревает еще до того, как его успевают довести до конца. Было решено начать работу с того, что авторы проекта умели лучше всего, — написания HTML/CSS кода.

Проект получил название `Лего`.

### Структура репозитория Лего
На верхнем уровне репозитория осуществлено первичное разделение - по технологиям.

```
css/
html/
js/
xml/
xsl/
```

У каждой папки технологии своя структура.

CSS распределяется в три разные директории:

```
css/
  block/
    b-dropdown/
      b-dropdown.css
  service/
    auto/
      block/
        b-head-logo-auto.css
      head.css
  util/
    b-hmenu/
      b-hmenu.css
```

 1. `block` — общепортальные блоки;
 2. `util` — блоки, которые имеют смысл вне Яндекса, их можно выложить в open source;
 3. `service` — стили для конкретных сервисов Яндекса. Подключив их к сервису, можно отобразить шапку или подвал.

Структура директории HTML аналогична CSS.

```
html/
  block/
    b-dropdown.html
  service/
    auto/
      l-head.html
  util/
    b-hmenu.html
```

JS находится в зачаточном состоянии:

```
js/
  check-is-frame.js
  check-session.js
  clean-on-focus.js
  dropdown.js
  event.add.js
  event.del.js
```

У каждого сервиса есть XML-файл, использующийся для построения шапки.

```
xml/
  block/
    b-head-tabs-communication.xml
    common-services.ru.xml
    head-messages.ru.xml
  service/
    auto/
      head.xml
```

XSL блоков лежит в одной директории:

```
xsl/
  block/
    b-dropdown.xsl
    b-head-line.xsl
    i-common.xsl
    i-locale.xsl
    l-foot.xsl
    l-head.xsl
```

Каждому блоку соответствует один файл.

`Лего` подключается на проекты через *svn:externals*.

При сборке для продакшна, код библиотеки полностью включается в проект,
что можно сравнить со статической линковкой.

Такой подход позволяет выпускать версии сервисов с разными версиями `Лего`
и переходить на новую версию тогда, когда это удобно команде проекта.

Мы используем его до сих пор.

#### Файлы страниц
Файлы, подключавшиеся на страницах, состояли из `@import`'ов реализации блоков.

```css
@import url(../../block/l-head/l-head.css);
@import url(../../block/b-head-logo/b-head-logo.css);
@import url(../../block/b-head-logo/b-head-logo_name.css);
@import url(block/b-head-logo-auto.css);
```

Эти  `@import`’ы писались вручную.

Именование файлов еще не устоялось - мы пробуем разные варианты.

## Общепортальный фреймворк Лего 1.2 (2008)
Чуть позже, в рамках версии 1.2, был произведен рефакторинг, и структура `Лего` изменилась.

```
common/
  css/
  js/
  xml/
  xsl/
example/
  html/
service/
  auto/
    css/
    xml/
```

Убрано разделение на `util` и `block`, весь общий CSS лежит в `common/css`.

От идеи выноса кода в open source на тот момент отказались и вернулись к ней только через два года.

```
common/
  css/
    b-dropdown/
      arr/
        b-dropdown.arr.css
        b-dropdown.arr.ie.css
        b-dropdown.css
        b-dropdown.ie.css
```

Файлы для IE переименованы: было `-ie.css` - стало `.ie.css`.
Расширения файлов теперь могут состоять из нескольких слов.

Всё, что находилось в опциональном CSS (файлах `b-dropdown_arr.css`),
вынесено в директории (`arr/b-dropdown.arr.css`).

В основном файле блока стало меньше кода.

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

## БЭМ
В марте 2009 года выходит версия `Лего 2.0`.

Этим событием оканчивается `верстка независимыми блоками` и начинается `БЭМ`.

БЭМ — аббревиатура от Блок-Элемент-Модификатор. Это три ключевых сущности, которые мы используем при разработке веб-компонентов.

### Лего 2.0 (2009)
Что же принципиально изменилось с выходом версии 2.0?

Основное изменение — мы вывели вперед блоки, а не технологии.
Отныне блоки первичны, а технологии их реализации — вторичны.

Реализацию каждого блока разместили в отдельной директории, технологии —
это файлы внутри нее. Также появилась документация к блоку — файл
`.wiki` внутри блока.

Какими терминами мы тогда оперировали?

#### Терминология
**Независимый блок**, который может быть использован в любом месте страницы.

В XML он представлен тегом в неймспейсе `lego`:

```xml
<lego:l-head>
<lego:b-head-logo>
```

HTML-класс блока соответствует имени этого тега:

```xml
<table class="l-head">
<div class="b-head-logo">
```

CSS:

```css
.l-head
.b-head-logo
```

Все файлы (css, js, html, xsl), относящиеся к блоку, кладутся в его директорию:

```
common/
  block/
    b-head-logo/
      b-head-logo.css
      b-head-logo.xsl
      b-head-logo.js
      b-head-logo.wiki
```

Элементы в XML пишутся в неймспейсе `lego` без префикса:

```xml
<lego:b-head-logo>
    <lego:name/>
</lego:b-head-logo>
```

Класс в HTML — тоже без префикса.

```html
<div class="b-head-logo">
    <span class="name">Авто</span>
</div>

.b-head-logo .name { ... }
```

Файлы для вложенного элемента кладутся в отдельную директорию.

```
common/
  block/
    b-head-logo/
      name/
        b-head-logo.name.css
        b-head-logo.name.png
        b-head-logo.name.wiki
```

Модификаторы в XML представлены атрибутами в неймспейсе `lego`:

```xml
<lego:b-head-tabs lego:theme="grey">
```

В HTML используется дополнительный класс.

```xml
<div class="b-head-tabs b-head-tabs_grey">

.b-head-tabs_grey { ... }
```

Файлы для модификатора кладутся в отдельную директорию, с подчеркиванием в начале имени:

```xml
common/
    block/
        b-head-logo/
            _theme/
                b-head-logo_gray.css
                b-head-logo_gray.png
                b-head-logo_gray.wiki
```

#### Декларация используемых блоков
Все лего-компоненты проекта описываются в XML-файле.

```xml
<lego:page>
    <lego:l-head>
        <lego:b-head-logo>
            <lego:name/>
        </lego:b-head-logo>

        <lego:b-head-tabs type="search-and-content"/>
```

Из него генерируются CSS-файлы.

```css
@import url(../../common/block/global/_type/global_reset.css);
@import url(../../common/block/l-head/l-head.css);
@import url(../../common/block/b-head-logo/b-head-logo.css);
@import url(../../common/block/b-head-logo/name/b-head-logo.name.css);
@import url(../../common/block/b-head-tabs/b-head-tabs.css);
@import url(../../common/block/b-dropdown/b-dropdown.css);
@import url(../../common/block/b-dropdown/text/b-dropdown.text.css);
@import url(../../common/block/b-pseudo-link/b-pseudo-link.css);
@import url(../../common/block/b-dropdown/arrow/b-dropdown.arrow.css);
@import url(../../common/block/b-head-search/b-head-search.css);
@import url(../../common/block/b-head-search/arrow/b-head-search.arrow.css);
@import url(../../common/block/b-search/b-search.css);
@import url(../../common/block/b-search/input/b-search.input.css);
@import url(../../common/block/b-search/sample/b-search.sample.css);
@import url(../../common/block/b-search/precise/b-search.precise.css);
@import url(../../common/block/b-search/button/b-search.button.css);
@import url(../../common/block/b-head-userinfo/b-head-userinfo.css);
@import url(../../common/block/b-head-userinfo/user/b-head-userinfo.user.css);
@import url(../../common/block/b-user/b-user.css);
@import url(../../common/block/b-head-userinfo/service/b-head-userinfo.service.css);
@import url(../../common/block/b-head-userinfo/setup/b-head-userinfo.setup.css);
@import url(../../common/block/b-head-userinfo/region/b-head-userinfo.region.css);
@import url(block/b-head-logo/b-head-logo.css);
@import url(block/b-head-search/b-head-search.css);
```

На примере этого файла видно, что сначала указывается общий код,
а потом добавляются стили, чтобы привести лего-блоки к дизайну проекта.

Имена файлов элементов пишутся через точку: `b-head-logo.name.css`

Из XML-декларации генерируются и JS-файлы.

```js
include("../../common/block/i-locale/i-locale.js");
include("../../common/block/b-dropdown/b-dropdown.js");
include("../../common/block/b-search/sample/b-search.sample.js");
include("../../common/block/b-head-userinfo/user/b-head-userinfo.user.js");
```

А также XSL-файлы.

```xml
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

<xsl:import href="../../common/block/i-common/i-common.xsl"/>
<xsl:import href="../../common/block/i-items/i-items.xsl"/>
<xsl:import href="../../common/block/l-head/l-head.xsl"/>
<xsl:import href="../../common/block/b-head-logo/b-head-logo.xsl"/>
<xsl:import href="../../common/block/b-head-logo/name/b-head-logo.name.xsl"/>
<xsl:import href="../../common/block/b-head-tabs/b-head-tabs.xsl"/>
<xsl:import href="../../common/block/b-dropdown/b-dropdown.xsl"/>
<xsl:import href="../../common/block/b-pseudo-link/b-pseudo-link.xsl"/>
<xsl:import href="../../common/block/b-head-search/b-head-search.xsl"/>
<xsl:import href="../../common/block/b-search/b-search.xsl"/>
<xsl:import href="../../common/block/b-search/input/b-search.input.xsl"/>
<xsl:import href="../../common/block/b-search/sample/b-search.sample.xsl"/>
<xsl:import href="../../common/block/b-search/precise/b-search.precise.xsl"/>
<xsl:import href="../../common/block/b-search/button/b-search.button.xsl"/>
<xsl:import href="../../common/block/b-head-userinfo/b-head-userinfo.xsl"/>
<xsl:import href="../../common/block/b-user/b-user.xsl"/>
<xsl:import href="../../common/block/b-head-userinfo/service/b-head-userinfo.service.xsl"/>
<xsl:import href="../../common/block/b-head-userinfo/setup/b-head-userinfo.setup.xsl"/>
<xsl:import href="../../common/block/b-head-userinfo/region/b-head-userinfo.region.xsl"/>

</xsl:stylesheet>
```

Мы перестали писать эти файлы руками, началась генерация кода.

### Скорость селекторов (2009)
При реализации новой версии Яндекс.Почты была поставлена задача сделать ее быстрой как компьютерная программа.

Для решения задачи мы начали использовать XSL в браузере (и подгружать по сети XML, необходимый для отрисовки данных на странице) и столкнулись с проблемой, что трансформации отрабатываются быстро, но вставка в DOM полученного результата происходит очень медленно. При этом, если отключить CSS, всё происходит быстро.

Исследуя проблему, мы пришли к тому, что тормозят селекторы
CSS, которые при большом DOM-дереве и большой таблице стилей оказывают
существенное влияние на скорость отрисовки браузером страницы.

Результаты исследования подробно описаны в [статье](http://clubs.ya.ru/bem/replies.xml?item_no=338).

Мы поняли, что решение этой проблемы у нас есть давно — это
[абсолютно-независимые блоки](http://clubs.ya.ru/bem/replies.xml?item_no=43).

Мы перевели все блоки в `Лего` на АНБ-нотацию и с тех пор пишем их так, чтобы
у каждого DOM-узла был свой `class`, на который можно навесить стили. Также мы не
используем Tag Rules в CSS.

В классы элементов вносится имя блока, селекторы получаются простыми и быстрыми.

```html
<div class="b-head-logo">
    <span class="b-head-logo__name">
        Авто
    </span>
</div>
```

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

В именах файлов разделитель . был заменен на `__`. Было `b-block.elem.css` —
стало `b-block__elem.css`. Теперь они совпадают с CSS-селекторами.

Были реализованы модификаторы у элементов по аналогии с модификаторами блоков:
`.b-block__elem_theme_green` по аналогии с `.b-block_theme_green`.

В имя файла модификатора и в его класс внесен тип модификатора.
Было `.b-menu__item_current` — стало `.b-menu__item_state_current`.

Причина этого изменения — работа с модификаторами из JS.

## Open source (2010)
В 2010 году мы создали организацию [bem](https://github.com/bem) на GitHub,
чтобы вести разработку в open source.

### Библиотека bem-bl
Мы начали выносить блоки из `Лего` в [bem-bl](https://ru.bem.info/libs/bem-bl/),
проводя одновременно с этим рефакторинг.

Вместе с выносом блоков публиковали информацию про них в клубе:
[Блок, Элемент, Модификатор (БЭМ, BEM)](http://clubs.ya.ru/bem/posts.xml?tag=75173403).

Работы по выносу блоков в open source пока не закончены.

### Инструменты
Началась реализация инструментов [bem-tools](https://ru.bem.info/tools/bem/bem-tools/),
которые помогают работать с файлами по БЭМ-методам.

Инструменты реализуются на JavaScript под Node.js.

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

Например, на проекте может быть:

 1. Публичная библиотека блоков с GitHub;
 2. Внутренняя библиотека lego;
 3. Блоки самого проекта.

```
bem-bl/
  b-logo/
lego/
  b-logo/
auto/
  blocks/
    b-logo/
```

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

Для этого нужно сконфигурировать уровень:

```
.bem/
  level.js
```

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

### Шаблонизатор BEMHTML
После экспериментов с разными шаблонизаторами, был разработан
шаблонизатор [BEMHTML](https://ru.bem.info/technology/bemhtml/current/intro/).

Этот шаблонизатор

 1. позволяет писать шаблоны в БЭМ-терминах;
 2. доопределять их на уровнях переопределения;
 3. исполнять эти шаблоны как на сервере, так и в браузере, поскольку
 шаблоны компилируются в простой и быстрый JavaScript.

О BEMHTML есть много информации в клубе на Яру:

 * <http://clubs.ya.ru/bem/replies.xml?item_no=898>
 * <http://clubs.ya.ru/bem/replies.xml?item_no=899>
 * <http://clubs.ya.ru/bem/replies.xml?item_no=1153>
 * <http://clubs.ya.ru/bem/replies.xml?item_no=1172>
 * <http://clubs.ya.ru/bem/replies.xml?item_no=1391>

## Резюме
Как вы можете видеть, БЭМ появился не сразу.
У нас был долгий период проб и подбора наиболее подходящего нам варианта.

Но обращаем ваше внимание, что всё это время это всё же был БЭМ.

То, что мы используем сейчас, — не единственно верное решение.
Наоборот, мы рекомендуем использовать БЭМ в ваших проектах в том объеме, в котором он принесет наибольшую пользу.

Гибкость БЭМ-методологии позволяет вам настраивать ее под свои текущие процессы.
Это способ организации работы над проектом, который позволяет команде работать c единым кодом и говорить на одном языке.

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

Например, у вас есть проект, в котором вы хотите применить БЭМ только для верстки.

Хорошо, мы тоже с этого начинали.

Выбирайте подходящую вам схему...

Например, с префиксами и каскадом.

```css
.b-block
.b-block .elem
.b-block_size_l
.b-block .elem_size_l
```

Или задайте класс каждому DOM-узлу и используйте абсолютно-независимые блоки.

```css
.b-block
.b-block__elem
.b-block_size_l
.b-block__elem_size_l
```

Или можно убрать префиксы.

```css
.block
.block__elem
.block_size_l
.block__elem_size_l
```

И начинайте делать верстку на проекте согласно БЭМ.

Читайте отдельно об [организации файловой системы](/method/filesystem/).

<a name="decl"></a>
## Декларация блока

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

В терминах парадигмы **объектно-ориентированного программирования**:

 * блок — класс;
 * экземпляр блока — экземпляр класса.

В соответствии с ООП, вся функциональность блока реализуется модульно в методах класса *(=блока)*.

Методы блока подразделяются на:

 * методы экземпляра блока;
 * статические методы.

Код блока в `i-bem.js` принято называть **декларацией**, чтобы подчеркнуть принятый
в БЭМ декларативный стиль программирования.

Поведение блока программируется в декларативном стиле в виде утверждений: `набор условий` — `реакция блока`.

<a name="decl-syntax"></a>
### Синтаксис декларации

#### Блоки с DOM-представлением

##### Объявление нового блока без родителя

Чтобы задекларировать новый JS-блок **с DOM-представлением** (привязанный к HTML-элементу), нужно воспользоваться методом `decl` [ym][]-модуля `i-bem__dom`.

Метод `decl` принимает аргументы:

1. Описание блока `{String}` или `{Object}`.
2. Методы экземпляра блока — `{Object}`.
3. Статические методы — `{Object}`.

Объявленные методы будут применяться во всех экземплярах блока независимо от их состояний (модификаторов).

**Пример:** Декларация методов для блока `button`.

```js
modules.define('button', ['i-bem__dom'], function(provide, BEMDOM) {

provide(BEMDOM.decl(this.name,
    {
        /*методы экземпляра*/
    },
    {
        /*статические методы*/
    })
);

});
```

Поле контекста `ym` `this.name`, передаваемое первым аргументом методу `BEMDOM.decl`, содержит ссылку на имя блока, указанное первым аргументом `modules.define`.

<a name="bem-decl"></a>
#### Блоки без DOM-представления

Для декларации блоков без DOM-представления служит метод `decl` [ym][]-модуля `i-bem`.

Метод принимает те же параметры, что и метод `decl` модуля `i-bem__dom`:

```js
modules.define('my-block', ['i-bem'], function(provide, BEM) {

provide(BEM.decl(this.name,
    {
        /*методы экземпляра*/
    },
    {
        /*статические методы*/
    })
);

});
```


***

**NB** Оформлять инфраструктурный код в виде блока без DOM-представления удобно, если в нем планируется использовать API БЭМ-блоков (состояния, выражаемые модификаторами, БЭМ-события и
т. п.). Если использовать БЭМ-предметную область не планируется,
инфраструктурный код можно оформлять в виде [ym][]-модуля.

**Пример:**

```js
modules.define('router', function(provide) {

provide({
    route : function() { /*... */ }
});

});
```


***

<a name="inher"></a>
### Наследование блока

Одна и та же функциональность может быть востребована в нескольких блоках проекта.
Например, разные блоки могут обращаться за данными к бэкенду, используя AJAX,
или совершать однотипные операции с DOM-деревом и т.д. Чтобы избежать ненужных повторов в коде, общую функциональность можно инкапсулировать в виде модулей, а затем добавлять к блокам.

Наследование позволяет повторно использовать функциональность блока, расширяя ее новой логикой.
В `i-bem.js` доступно несколько механизмов наследования. Выбор конкретного механизма зависит от специфики создаваемого блока.

<a name="inher-simple"></a>
#### Простое наследование

В случае простого наследования создаваемый блок объявляется как наследник существующего. Для этого нужно:

1. Указать базовый блок в зависимостях модульной системы.
2. Передать ссылку на базовый блок в специальном поле `baseBlock` декларации.

Например, блок `bblock` наследуется от блока `ablock`:

```js
modules.define('ablock', ['i-bem__dom'], function(provide, BEMDOM) {

provide(BEMDOM.decl(this.name, {}));

});

modules.define('bblock', ['i-bem__dom', 'ablock'], function(provide, BEMDOM, ABlock) {

provide(BEMDOM.decl({ block : this.name, baseBlock : ABlock }));

});
```


Такой механизм позволяет использовать методы базового блока внутри производного.
Для вызова одноименных методов базового блока служит [вспомогательное свойство](i-bem-js-context.ru.md#spec-fields) `this.__base`.

***

**NB** В `i-bem` можно создавать цепочки наследования – блок наследуется от другого,
который, в свою очередь, наследуется от третьего и т.д.

***

<a name="inher-over"></a>
#### Доопределение блока

Чтобы создать вариант уже существующего блока с измененной или дополненной функциональностью, можно **доопределить** базовый блок на *уровне переопределения* проекта.

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

```js
modules.define('ablock', ['i-bem__dom'], function(provide, BEMDOM) {

provide(BEMDOM.decl(this.name, {})); //Объявляем базовый блок

});

modules.define('ablock', function(provide, ABlock) {

provide(ABlock.decl({})); //Доопределяем базовый блок

});
```


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

<a name="inher-over-modifier"></a>
##### Добавление модификатора к блоку

В соответствии с БЭМ-методологией состояния блока должны описываться [модификаторами](i-bem-js-states.ru.md#modifiers).
Поэтому чтобы расширить функциональность блока часто нужно реализовать поддержку новых модификаторов.

Для добавления модификатора необходимо передать методу `decl` доопределяемого блока:

* хэш с ключами `modName` и `modVal`. Значением для `modName` служит строка – имя модификатора. Значением `modVal` – строка со значением модификатора.
* хэш методов, которые будут доступны для блока с соответствующим модификатором. При наличии  одноименных методов и модификаторов, будет использована их реализация из хэша.

```js
modules.define('ablock', ['i-bem__dom'], function(provide, BEMDOM) {

provide(BEMDOM.decl(this.name, {})); //Объявляем базовый блок

});

modules.define('ablock', function(provide, ABlock) {

provide(ABlock.decl({ modName : 'm1', modVal : 'v1' }, {})); //Доопределяем базовый блок с модификтором _m1_v1

});
```


***

**NB** [Cтатические методы][context] блока будут доступны всем его экземплярам *вне зависимости от значений модификаторов*.
Модификаторы — это свойства экземпляра блока, а статические методы принадлежат классу
блока и не учитывают состояния модификаторов.

***

<a name="inher-mixins"></a>
#### Блоки-миксы

В `i-bem.js` для добавления востребованной функциональности к блокам используется специальный
тип блоков – **блоки-миксы**. Главная особенность блоков-миксов состоит в том, что они не участвуют в цепочке наследования. Это позволяет примешивать реализованную в них функциональность к другим блокам без риска нарушить их [связи с родительскими блоками](i-bem-js-context.ru.md#spec-fields) (`this.__base`).

<a name="inher-mixins-declwithmix"></a>
##### Примешивание блока-микса

Чтобы примешать к блоку один или несколько блоков-миксов, необходимо в декларации блока присвоить значение опциональному полю `baseMix`. Значением служит массив строк – имен примешиваемых блоков-миксов:

```js
modules.define('my-block', ['i-bem__dom', 'foo', 'bar'], function(provide, BEMDOM) {

provide(BEMDOM.decl({ block : this.name, baseMix : ['foo', 'bar']},
    { /*методы экземпляра*/ },
    { /*статические методы*/ }
));

});
```


<a name="inher-mixins-mixindecl"></a>
##### Декларация блока-микса

В качестве блоков-миксов можно использовать только блоки, созданные с помощью `declMix`.
Метод принимает декларацию блока в формате, аналогичном методу `decl`.

```js
modules.define('mymix', ['i-bem__dom'], function(provide, BEMDOM) {

provide(BEMDOM.declMix('mymix', //только строка с именем
    { /*методы экземпляра*/ },
    { /*статические методы*/ }
));

});
```


***

**NB** Блок-микс нельзя инстанцировать и использовать как самостоятельный блок.

***

<a name="trigger-decl"></a>
#### Декларация триггеров

[Триггеры][states], выполняемые при установке модификаторов, описываются в декларации блока. Для этого в хэше методов экземпляра блока зарезервированы свойства:

* `beforeSetMod` — триггеры, вызываемые до установки **модификаторов блока**;
* `beforeElemSetMod` — триггеры, вызываемые до установки **модификаторов элементов**;
* `onSetMod` — триггеры, вызываемые после установки **модификаторов блока**;
* `onElemSetMod` — триггеры, вызываемые после установки **модификаторов элементов** блока.

```js
modules.define('block-name', function(provide, BEMDOM) {

provide(BEMDOM.decl(this.name,
    {
        /*методы экземпляра*/
        beforeSetMod: { /*триггеры до установки модификаторов блока*/},
        beforeElemSetMod: { /*триггеры до установки модификаторов элементов*/},
        onSetMod: { /*триггеры после установки модификаторов блока*/ },
        onElemSetMod: { /*триггеры после установки модификаторов элементов*/ }
    },
    {
        /*статические методы*/
    }
));

});
```


Значение свойств `beforeSetMod` и `onSetMod` — хэш, связывающий изменения модификаторов с триггерами. Триггер получает аргументами:

* `modName` – имя модификатора;
* `modVal` – выставляемое значение модификатора;
* `prevModVal` – предыдущее значение модификатора. Для `beforeSetMod` это текущее значение модификатора, которое будет заменено на `modVal`, если триггер не вернет `false`.

```js
BEMDOM.decl(this.name, {
    onSetMod: {
        'mod1': function(modName, modVal, prevModVal) { /*... */ }, //установка mod1 в любое значение
        'mod2': {
            'val1': function(modName, modVal, prevModVal) { /*... */ }, //триггер на установку mod2 в значение val1
            'val2': function(modName, modVal, prevModVal) { /*... */ }, //триггер на установку mod2 в значение val2
            '': function(modName, modVal, prevModVal) { /*... */ } //триггер на удаление модификатора mod2
        },
        'mod3': {
            'true': function(modName, modVal, prevModVal) { /*... */ }, //триггер на установку простого модификатора mod3
            '': function(modName, modVal, prevModVal) { /*... */ } //триггер на удаление простого модификатора mod3
        },
        '*': function(modName, modVal, prevModVal) { /*... */ } //триггер на установку любого модификатора в любое значение
    }
})
```


Для триггера на установку любого модификатора блока в любое значение
существует сокращенная форма записи:

```js
beforeSetMod: function(modName, modVal, prevModVal) { /* ... */ }
onSetMod: function(modName, modVal, prevModVal) { /* ... */ }
```


Триггеры на установку **модификаторов элемента** описываются в свойствах `beforeElemSetMod` и `onElemSetMod`. Хэш в значениях свойств имеет дополнительный уровень вложенности — **имя элемента**.
В качестве аргументов триггеру передаются:

* `elem` — имя элемента;
* `modName` – имя модификатора;
* `modVal` – выставляемое значение модификатора;
* `prevModVal` – предыдущее значение модификатора. Для `beforeSetMod` это текущее значение модификатора, которое будет заменено на `modVal`, если триггер не вернет `false`.

```js
BEMDOM.decl(this.name, {
    onElemSetMod: {
        'elem1': {
            'mod1': function(elem, modName, modVal, prevModVal) { /*... */ }, //триггер на установку mod1 элемента elem 1 в любое значение
            'mod2': {
                'val1': function(elem, modName, modVal, prevModVal) { /*... */ }, //триггер на установку mod2 элемента elem1 в значение val1
                'val2': function(elem, modName, modVal, prevModVal) { /*... */ } //триггер на установку mod2 элемента elem1 в значение val2
            }
        },
        'elem2': function(elem, modName, modVal, prevModVal) { /*... */ } //триггер на установку любого модификатора элемента elem2 в любое значение
    }
})
```


Сокращенная запись для триггера на установку любого модификатора элемента `elem1` в любое значение:

```js
beforeElemSetMod: { 'elem1': function(elem, modName, modVal, prevModVal) { /* ... */ } }
onElemSetMod: { 'elem1': function(elem, modName, modVal, prevModVal) { /* ... */ } }
```


[ym]: https://github.com/ymaps/modules

[i-bem]: https://ru.bem.info/libs/bem-core/current/desktop/i-bem/jsdoc/

[i-bem__dom]: https://ru.bem.info/libs/bem-core/current/desktop/i-bem/jsdoc/

[decl]: ./i-bem-js-decl.ru.md

[context]: ./i-bem-js-context.ru.md

[states]: ./i-bem-js-states.ru.md


![GitHub Logo](/images/logo.png)
Format: ![Alt Text](url)

> А это некая цитата
И продолжение цитаты

- [x] @mentions, #refs, [links](), **formatting**, and<del>tags</del>supported
- [x] list syntax required (any unordered or ordered list supported)
- [x] this is a complete item
- [ ] this is an incomplete item



First Header | Second Header
------------ | -------------
Content from cell 1 | Content from cell 2
Content in the first column | Content in the second column


#1
mojombo#1
mojombo/github-flavored-markdown#1

@mention

i@tadatuta.com


http://www.github.com/

перед ~~зачеркнутый текст~~ после

# эмоджи
:sparkles: :camel: :boom:
