Развитие БЭМ методологии

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


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

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

Давайте вернемся назад в 2005 год и если мы взглянем на очень-очень занятых интерфейсных разработчиков Яндекса, то увидим …

… с  чего ВСЕ ЭТО началось

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

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

Для каждой страницы существовал статический HTML файл со всеми CSS стилями, собранными в одной таблице, project.css, и весь JavaScript помещенный в один project.js файл, оба доступные всем страницам проекта. Еще в 2005 году, 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) */

Как идентификаторы, так и имена классов были использованы в HTML верстке.

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

Проекты Среднего Класса

К началу 2006 года, первая версия Yandex.Music разрабатывалась очень вяло. Несколько страниц, каждая отличалась от других, не вписывались в знакомые упрощенные понятия. Десятки CSS классов, которым нужно было придумать содержательные имена, растущее количество непроизвольных зависимостей распространяемых по всему проекту — все это взывало к лучшему решению.

Вот типичный кусок 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) */

Длинные каскадные правила использовались по всему коду.

Обратите внимание на другой пример:

/* Background images (begin) */ .b-foot div { height: 71px; background: transparent url(../i/foot-1.png) 4% 50% no-repeat; } .b-foot div div { background-position: 21%; background-image: url(../i/foot-2.png); } .b-foot div div div { background-position: 38%; background-image: url(../i/foot-3.png); } .b-foot div div div div { background-position: 54%; background-image: url(../i/foot-4.png); } .b-foot div div div div div { background-position: 71%; background-image: url(../i/foot-5.png); } .b-foot div div div div div div { background-position: 87%; background-image: url(../i/foot-6.png); } /* Background images (end) */

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

В то же время, начинался еще больший проект wow.ya.ru: блог-платформа — место для людей, где можно взаимодействовать, делиться, читать и участвовать в чем-либо.

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

Спасительные блоки

Нам нужно было указать данные домена для управления объектами на странице интерфейса. Это была проблема методологии: мы должны были прояснить то, как мы работали с такими понятиями, как классы, теги, визуальные компоненты, и т.д.

Для обычной веб-страницы в Яндекс проекте, HTML структура и CSS стили были по-прежнему в центре внимания наших усилий в области развития, а JavaScript являлся дополнительной технологией. Для того чтобы было легче поддерживать HTML и CSS многих компонентов, был придуман новый термин: “блок”. Блок  был частью дизайна страницы или макета, чья особенность и уникальный смысл определялся либо семантически, либо визуально.

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

CSS классы для блоков получили префиксы (b- c- g-), чтобы обеспечить своего рода имитацию пространства имен в CSS. Само соглашение по присвоению имен было изменено позже, но вот первоначальный список, с примечаниями::

  • b- (блок). Независимый блок, размещенный на странице, где он вам необходим.
  • с- (контроль). Контроль (т.е. независимый блок), с объектом JavaScript привязанный к нему.
  • g- (глобальный). Глобальное определение, использовалось в редких случаях и всегда определялось для конкретной, уникальной цели. Число этих определений было сведено к минимуму.

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

  • -nojs (без JavaScript). Правило стиля, которое должно применяться при отключенном JavaScript. Обратный .onload вызов может удалить эти суффиксы со всех DOM узлов, семантически помечая их как «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 и файлы изображений будут храниться в отдельных папках. В CSS были специальные файлы для временных решений (обходных путей) в IE, которые помогали сохранять основной код чистым, а также придерживания стандартам. В рабочей версии, ИЕ получит свой заслуженный CSS «костыль» в виде ИЕ-специфичных условных комментариев.

В настоящее время 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-family: Arial, sans-serif; font-size: 0.8em; padding: 0 0 2em 0; background: #fff; } * html body { font-size: 80%; }

Невалидные обходные пути были помещены в yaru-ie.css файл (который загружался только с условными комментариями IE).

/* 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'); }

Разработка фреймворка: начало

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

Первые фрагменты страницы, которые нужно было объединить это верхний и нижний колонтитулы, а также некоторые типографические CSS элементы. Соответствующие файлы были размещены на внутреннем выделенном сервере (common.cloudkill.yandex.ru в списке, указанном ниже). Это были первые дни нашего единого фреймворка.

Стили могут быть импортированы непосредственно с сервера:

@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 { … }

Очевидно, что было проведено слишком много импортов! Таким образом, мы решили предварительно компилировать стили (а, позже, JavaScript-файлы) перед развертыванием. Компиляция будет заменять @import директивы вместе с фактическим содержимым файла (этот процесс называется «встраивание») и будет выполнять оптимизацию. Наш внутренний инструмент встраивания превратился из простой оболочки скрипта в проект с открытым кодом, Borschik. Посмотрите!

Независимые блоки как концепт

К осени 2007 года, к нашей повседневной практике добавилось немного теории. Понятие независимых блоков и основная идея нашего понимания HTML и CSS макетов, была представлена на клиентской конференции 2007 года в Москве, Россия.

В этой презентации была сделана первая попытка определить блок.

Блоки: декларация независимости

В нашей попытке получить формальное (на самом деле, полуформальное) определение блока, были выделены следующие три принципа:

  1. Только имена классов (не ID) должны использоваться для CSS.
  2. Имя класса каждого блока должно иметь пространство имен (префикс).
  3. Каждое правило CSS должно принадлежать блоку.

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

Простые и сложные блоки: ошибочная классификация

Мы определили «простые» блоки как те, которые не могут удерживать другие блоки где-либо внутри. С другой стороны «сложные» блоки могут (и это даже требуется) иметь вложенные блоки.

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

Полностью независимые блоки

CSS определения были неверными, когда мы смешали большое количество стилизованного контента из разных источников на одной странице. В сложных макетах, блоки могут изменять вид друг друга из-за конфликтов в названиях элементов. Имя тега на основе CSS правил могут соответствовать большему количеству узлов, чем предполагалось. Таким образом, была определена более строгая версия независимого блока (под названием «полностью независимый блок», или CIB - «completely independent block») со следующими правилами:

  1. Никогда не сочетать CSS с именами тегов. Для всего используйте имена классов .b-user b → .b-user .first-letter
  2. Имена классов для блочных элементов должны начинаться с имени блока родителя .b-user .first-letter → .b-user-first_letter

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

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

Префиксы

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

  • b- Common blocks
  • h- Holsters (используемые для объединения нескольких элементов)
  • l- Layout grids
  • g- Global styles

Модификатор

«Модификатор» может быть определен как особое состояние блока, флажок выполнения конкретного свойства.

Лучше всего объяснить на примере. Блок, представляющий кнопку, может иметь три размера по умолчанию: маленький, нормальный и большой. Вместо создания трех различных блоков, вы назначаете блоку модификатор. Модификатор запросит имя (например, size) и значение (small normal или big).

Есть две причины для блока изменить представление своего состояния:

  1. Презентация блока может быть изменена из-за его размещения в макете. Это называется «контекстно-зависимой» модификацией.
  2. Дополнительное (постфиксное) имя класса может изменить внешний вид блока путем применения дополнительных CSS правил. Это был модификатор «независимый от контекста» .class="b-block b-block-postfix"

ЕДИНЫЙ фреймворк в портале

В начале 2008 года Яндекс серьезно взялся за пересмотр своей внутренней политики разработки. Мы решили создать руководство (для внутреннего использования) для применения лучших методик в дизайне интерфейса и компании в целом.

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

Интерфейсы развиваются быстро, так быстро, что любая долгосрочная попытка описать интерфейсы словами и изображениями будут устаревать еще до завершения. Нам нужно было руководство по применению и созданию фирменного стиля, которое бы представляло наши интерфейсы: быстро меняющиеся, а также же объединенные между различными службами и продуктами Яндекса.

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

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

Структура репозитория фреймворка: первый подход

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

 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 Блоки для общих целей, готовые иметь открытый код.
  3. service Это были CSS стили для конкретных служб Яндекса, используемых для разработки и продвижения брэнда, а также верхнего и нижнего колонтитулов, и т.д.

Структура папки HTML была идентична CSS:

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

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

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

У каждой службы был соответствующий XML файл, который семантически описывал заголовок страницы (что предоставило особо необходимые данные проекта). Вместе с XSL таблицей стилей, XML файла было достаточно для генерации HTML кода заголовка.

 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 

А как насчет интеграции?

Lego был привязан к проектам с помощью функции управления версиями, известной как svn:externals.

Когда модуль был создан для развертывания в готовой рабочей версии, код из внешней библиотеки (Lego) был встроен в модуль, похожий на статическую библиотеку связывая скомп …

Если вы хотите прочитать полностью статью, посетите сайт наших спонсоров

Comments are closed.