Pull to refresh

BEM с человеческим лицом

Reading time 9 min
Views 23K

Звучная аббревиатура BEM пришла к нам из лабораторий Яндекса. Там, как и в случае с XSLT, применение BEM решили возвести в абсолют: под BEM'ом в Яндексе понимают целое семейство утилит и подходов, объединенных единой идеологией блочной архитектуры веб-приложений. Как любая тоталитарная система, BEM требует соблюдения строгих правил при разработке, не редко вступающих в конфликт со здравым смыслом небольших проектов, не сравнимых по ресурсам с Яндексом. И да, то самое чувство, когда читаешь официальные доки по BEM.


Однако, как часто бывает в процессе эволюции больших систем, под давлением интеллекта и безлимитных сроков рождается технологический алмаз, настолько же маленький и самостоятельный, насколько и ценный, который огранят уже другие. Да, BEM с его спасительной строгостью — это явное откровение. Каждый, кто на моих глазах причащался, мгновенно становился счастливым. Однако, после первой волны наслаждения приходит осознание, что второй подход к этому снаряду может порвать ментальные связки по всему объему мозга. И вот уже слышны жалобы на слишком большую сложность освоения, на чрезмерную многословность, на (внимание!) увеличение количества мегабайт в HTML и CSS, и кто знает на что еще, не относящееся к делу.


Соглашусь, трудно взять и начать писать BEM без разбега: и нотация глаз колет, и старые трюки не проходят, и думать приходится системно. И вообще, писали как-то годами без BEM'а и писать будем! А ведь для легкого и непринужденного преодоления порога вхождения нужно сделать всего два движения. Во-первых, понизить сам порог, смягчив BEM. И во-вторых, немного подтянуться самим. Тогда переход будет ровненьким и мы мягко вкатимся в эру читабельного и поддерживаемого CSS.


Как мы дошли до жизни такой?


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


Немного контекста нам не помешает, особенно исторического. Чтобы лучше понять куда нас ведет BEM, стоит сначала вспомнить, откуда мы идём. Итак, коротко приведу этапы становления стиля написания CSS.


1995: Доисторическая эра


<font color="red">

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


2000: Примитивизм


h3 { color: red }

Ура, можно взять и всё перекрасить одной правкой. Раскладка всё еще делается таблицами, но уже подкрадываются флоаты. Пузырь доткомов лопнул, пора подумать об эффективности и поддержке.


2005: Мы умеем в селекторы!


div p.red h3 { color: red }

Вот оно, зарево того пожара, в хаосе которого многие из нас сгорают на работе и по сей день. Тогда сайты уже умели делать не только студенты технических вузов, но и матёрые системные программисты. Они развивали фреймворки для бекенда, правильно проектировали базы данных, учились держать нагрузку, и даже создавали nginx! Но при этом совсем не умели и не хотели уметь CSS, или, тем более, этот ваш мерзкий JavaScript. Это привело к Bootstrap и jQuery.


2007: Семантический CSS


.article.new .title { color: red }

В тред заядлых бекендеров врывается pepelsbey и несет доброе, светлое, чистое. Избавляет от тегов в CSS, открывает глаза на значение оных в HTML, вдохновляет на здоровый образ мысли и кода во фронтенде. Больше микроформатов и хорошего, годного веба, как его задумывал создатель.


2010: Назад в каменный век вместе с Twitter Blueprint


<p class="text-left clearfix red">

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


2015: BEM


.article__header--new { color: red }

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


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


Один шаг назад, два шага вперед


Что главное нам надо от BEM'а:


  1. Отсутствие коллизий
  2. Отсутствие коллизий
  3. И еще раз отсутствие коллизий

BEM гарантирует отсутствие коллизий с помощью изоляции через пространства имён. Старая добрая концепция изолированных модулей. А с SASS вообще целый ООП. Конечно, сначала надо хорошенько подумать над разбиением на компоненты, но сперва давайте порадуемся дарам модульности:


  1. Читаемый и поддерживаемый код
  2. Переиспользование компонентов
  3. Простота тестирования
  4. Гармония с объектной природой JavaScript

Вот и всё. Если нам удасться сохранить главную ценность BEM'а — изоляцию компонентом, то творить внутри самих компонентов можно будет всё что угодно. Если вы контролируете все корневые стили, то есть у вас весь-весь код разбит на пространства имён, и за их пределами селекторов нет, то вы достигли просветления и можете позволить себе всё.


Нельзя, но если очень хочется…


Как же можно очеловечить BEM? А очень просто: надо немного расслабиться, вернуться к здравому смыслу и вспомнить чувство фана от экспериментов. От простого к сложному, от очевидного к интригующему.


Развидеть двойное подчеркивание


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


.Article { … }
.Article-title { color: red }

Еще чуть-чуть и станет совсем похоже на сексуальный рубёвый DSL.


Лаконичные модификаторы


Если следовать правилу «никогда не нарушать границы пространств имён» и каждому блоку выделять по отдельному элементу, то есть вместо двух классов на ноду:


<div class="Box Article">

делать две ноды на два класса:


<div class="Box"><div class="Article">

то можно упростить именование модификаторов до привычных:


.Article.new { … }

А если добавить им префикс is-, то стили можно прямо читать вслух:


.Article.is-new { … }

Дело, конечно, не за префиксами модификаторов, это всего-лишь приятный бонус. Главное, что у блоков Box и Article не будет конфликтов даже если захотеть, и не придется ловить баги от того, что иногда модуль Article загружается раньше модуля Box и его стили получают другой приоритет. Но это и так очевидно же!


Каскадность


Можно вернуть в ваш BEM немного CSS:


.Article.is-new .Article-title { color: red }

Радует то, что дальше одинарного вложения BEM вас никак не пустит по определению (не повторять структуру HTML-дерева), так что такое уже невозможно:


.Article .Article-title .Article-name { … }

И конечно, совсем нельзя вмешиваться в чужое пространство имен:


.Article .SubmitButton .Icon { … }

Иногда, по старой плохой привычке очень-очень хочется так сделать. Но нельзя. Баланс с хаосом надо держать, а иначе вы нарушите основу целостности BEM'а и все старания пойдут насмарку.


Мудрые замечания ArmorDarks и колкий стёб vintage просто вынуждают обратить внимание читателя на то, что деревья такими модификаторами не стилизуют: сломаются все дочерние элементы узла с таким модификатором. Стилизовать деревья надо еще строже — прямо по месту. Уверен, что читатели использующие рекурсивные структуры на вебе наслаждаются каскадностью CSS каждый день.


Не использовать BEM


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


.WYSIWYG {
  h1 { … }
  p { … }
  ul li { … }
}

Строгая культура других модулей не позволит им вмешаться во внутренние дела модуля WYSIWYG и всё будет хорошо. Конечно, вложить какой-то еще компонент в «непослушный» компонент не получится без вероятности коллизий, так что такие «непослушные» блоки могут быть только листьями в дереве компонентов: их можно вкладывать в любые компоненты, но не любые компоненты можно вкладывать в них.


Такой трюк бывает полезен не только в случае встраивания непредсказуемого пользовательского контента. Любой jQuery-плагин легко оборачивается в блок-изолятор и не благоухает своим CSS на всю страницу. Еще встречаются ну очень большие компоненты, например табличные таблицы в таблице таблиц, где строгое следование BEM'у заживо похоронит верстальщика под гнетом префиксов, так что дешевле всунуть Bootstrap. А еще случаются сложные формы, которые тоже удобнее и быстрее сделать листовыми элементами в блоках-изоляторах, а потом уже думать.


Базовые стили


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


Приняв это, становится очевидно, что старый добрый SMACSS подход к разделению стилей отлично портируется на BEM. Базовые стили остаются как есть. Layout становится просто специальным нелистовым компонентом. Модули, они же компоненты, они же блоки. Состояния проецируются на модификаторы один в один. Поддержка тем в BEM'е как бы вообще нативная. А состоянием в современном приложении должна управлять доменная логика и потом рендерить модель через React (руки прочь от jQuery!).


Итого


И вот уже стало всё знакомо и совсем не страшно. Можно даже сказать, что BEM с человеческим лицом — это старый добрый CSS для семантически правильного HTML, только легче поддерживается, проще пишется, лучше сжимается и быстрее исполняется.


Голый BEM — это прокрустово ложе, но если его понять, принять и допилить, то он становится сиденьем истребителя — тесновато, но зато на нем можно летать!


Письмо позвало в дорогу


@ArmorDarks пишет:


Возможно, Вам будет интересно: More Transparent UI Code with Namespaces

Спасибо, еще как интересно!


Насколько я понимаю, описанные приёмы помогают писать читаемый HTML, используя и пополняя готовый CSS. Например, утилитарный класс u-font-size-large применяется к ноде, чтобы сделать шрифт побольше, а t-light для того, чтобы применить к кнопке тему с более светлым фоном. В целом, если знать весь набор CSS правил, то HTML код действительно легко читать и писать. Однако, при смешивании стилей с одинаковым приоритетом вскоре потребуется !important, который описан в статье, как иногда приемлемый подход. На это я пойтить не могу, isagalaev воспитывал нас иначе.


Меня в BEM тронула именно строгая изоляция стилей от HTML. Раз CSS всё равно надо знать и понимать, то почему бы, наконец, не убрать всю работу со стилями в CSS? Например, если использовать .u-font-size-large для заголовка статьи, то так:


%u-font-size-large { font-size: x-large }
.Article-title { @include %u-font-size-large }

В статье приводится пример, что могут потребоваться дополнительные мелкие правки стилей, даже тысячи их. И автор сразу верно возражает, что такие мелкие правки говорят о проблемах с дизайном. Но надо так надо, давайте добавим .u-font-size-large-xx и не придется плодить всякие .article__title--large-xx, .post__title--large-xx, .user__title--large-xx. Здесь я на стороне BEM'а с его explicit против implicit, и вот почему.


Во-первых, вот нашли вы в чужом коде <div class="Article-title is-xx"> навели инспектором, и первые же стили будут именно модификатора, как наиболее специфичные. В случае .u-font-size-large-xx придется немного полистать и подумать. Далее. Если вы определили .Article-title.is-xx через наследование @extends .u-font-size-large то увидели в инспекторе же и .u-font-size-large, и другие блоки, использующие этот же суперкласс. А если вы предпочли @include, что в этом случае, возможно, логичнее, то уже прыгнули сразу в файл Article.scss и вот она логика вашего модификатора, возможно, даже более сложная, но наглядная и в терминах SCSS.


Во-вторых, не нужно думать о том, какие могут быть сочетания разных .u-*, вот же они все для конкретно этого блока — лежат в файле Article.scss гарантируя правильную семантику. Все возможные сочетания модификаторов из какой-то библиотеки прямо перед глазами, хоть и несколько многословно. Считаю что это спасительный детерминизм. Такой вот Go vs C++.


В-третьих, когда меняется стиль, не нужно лезть в .erb, .inc, .php, .js шаблон и заменять .u-font-size-large на новый .u-font-size-large-xx во все места использования компонента Article, достаточно перекомпилить SASS. Конечно, в случае добавления семантически нового модификатора для нового состояния доменной сущности шаблоны таки придется поправить. Но это уже будет законная, понятная правка, с адекватным комментарием в коммите, со строкой в документации для нового состояния, с тестами без привязки к CSS.


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


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


Спасибо еще раз, лавина сошла.


@ArmorDarks пишет:


Такой подход (o-btn c-btn c-btn--positive qa-modal-accept прим. авт.) позволяет создавать большую часть уникальных страниц без написания единой новой строчки CSS.

Вот она ключевая фраза, спасибо вам за неё. Это водораздел, пропасть посреди, государственная граница, горизонт событий, наконец. Именно эта фраза вбивает кол между двумя совершенно противоположными и одновременно равноправными и дополняющими друг друга подходами к верстке. Лично я нахожусь на фанатичной стороне «поддерживать и масштабировать годами один сложный проект, постоянно меняя CSS», а ваша крайность «заготовить прочный фундамент и годами строить на нем большой массив, не касаясь CSS». Это как паттерны против фреймворков. Абстрактное против конкретного. Используя BEM как подход, люди строят библиотеки компонентов как фреймворки. Статья про подход. Пора бы уже автору прослушать курс теории категорий.


Кстати, как верно подсказывает документация к Bootstrap, такой фундаментальный CSS-фреймворк должен быть read-only, прямо как до появления CSS и тега <font> был веб: берёшь — и пишешь, используя только уже готовые и доступные теги HTML, не думая про CSS. Работало же. А вот если таки позволять менять такой CSS-фреймворк на уровне процесса, то есть по требованию бизнеса… GOTO 1.

Only registered users can participate in poll. Log in, please.
Оцените своё отношение к BEM по 128-бальной шкале:
25.87% Обожаю! 134
18.73% Ненавижу! 97
55.41% 42! 287
518 users voted. 150 users abstained.
Tags:
Hubs:
+5
Comments 56
Comments Comments 56

Articles