Битва с БЭМ: 10 самых распространенных проблем и способы их решения (часть 2)
Предлагаем вам вторую часть статьи о решении кажущихся сложностей в БЭМ-методике: в прошлой части мы уже разобрались, что делать с глубокой вложенностью элементов, со сложными именами и непростой структурой, как быть с элементами-обертками и кросс-компонентами, и попытались определиться, когда стоит создавать новый компонент. Давайте продолжим.
6. Как управлять состоянием?
Это довольно распространенная проблема, особенно когда вы работаете со стилями открытого или активного компонента. Предположим, наш компонент имеет активное состояние, то есть, когда мы кликаем по нему, он получает определенное оформление, которое выделяет его среди остальных, например толстую зеленую рамку. Как мы должны назвать этот класс?
У нас есть два варианта: либо отдельный класс, либо именование по БЭМ методике на уровне компонента:
<!-- отдельный класс -->
<div class=\"c-card is-active\">
[…]
</div>
<!-- BEM modifier -->
<div class=\"c-card c-card--is-active\">
[…]
</div>
БЭМ выглядит логичнее и позволяет быстрее понять, к какому именно элементу относится класс. С другой стороны, если у вас простой проект, в котором необходимо присваивать аналогичное оформление элементам различных компонентов, отдельный класс поможет вам сократить количество кода в ваших скриптах, избавляя от необходимости прописывать события для каждого элемента, имеющего активное состояние.
7. Когда не обязательно присваивать класс?
Довольно часто верстальщики, не привыкшие присваивать класс каждому элементу на странице, бывают ошеломлены количеством классов, используемых для создания сложного пользовательского интерфейса.
Обычно рекомендуется присваивать класс каждому элементу, который должен быть модифицирован в зависимости от контекста. Хотя допускается опущение классов у элементов вроде p, если они должны выглядеть одинаково в каждой части родительского элемента. Конечно, такой подход означает, что в вашей разметке будет очень много классов, однако это позволит сделать верстку максимально модульной, позволяя компонентам полноценно функционировать в любом окружении без появления побочных эффектов.
Учитывая глобальную природу CSS, можно утверждать, что именно присвоение классов всем элементам позволяет нам получить полноценный контроль над состоянием каждого компонента.
8. Как вкладывать компоненты?
Предположим, мы хотим разместить чек-лист какого-то процесса в нашем компоненте c-card. Это будет выглядеть как-то так:
<div class=\"c-card\">
<div class=\"c-card__header\">
<h2 class=\"c-card__title\">Title text here</h3>
</div>
<div class=\"c-card__body\">
<p>I would like to buy:</p>
<!-- Uh oh! A nested component -->
<ul class=\"c-card__checklist\">
<li class=\"c-card__checklist__item\">
<input id=\"option_1\" type=\"checkbox\" name=\"checkbox\" class=\"c-card__checklist__input\">
<label for=\"option_1\" class=\"c-card__checklist__label\">Apples</label>
</li>
<li class=\"c-card__checklist__item\">
<input id=\"option_2\" type=\"checkbox\" name=\"checkbox\" class=\"c-card__checklist__input\">
<label for=\"option_2\" class=\"c-card__checklist__label\">Pears</label>
</li>
</ul>
</div>
<!-- .c-card__body -->
</div>
<!-- .c-card -->
У нас появляется несколько проблем: во-первых, высокая вложенность селекторов; во-вторых, все стили, применяемые к c-card\_checklist\_item, ограничиваются этой областью видимости и не могут быть использованы повторно.
Обе проблемы просто исчезают, если мы перестаем рассматривать список, как элемент, вложенный в компонент, и начнем считать его отдельным компонентом. Здесь стоит использовать метку l из способа именования классов, который мы упоминали во втором пункте предыдущей статьи:
<div class=\"c-card\">
<div class=\"c-card__header\">
<h2 class=\"c-card__title\">Title text here</h3>
</div>
<div class=\"c-card__body\"><div class=\"c-card__body\">
<p>I would like to buy:</p>
<!-- Much nicer - a layout module -->
<ul class=\"l-list\">
<li class=\"l-list__item\">
<!-- A reusable nested component -->
<div class=\"c-checkbox\">
<input id=\"option_1\" type=\"checkbox\" name=\"checkbox\" class=\"c-checkbox__input\">
<label for=\"option_1\" class=\"c-checkbox__label\">Apples</label>
</div>
</li>
<li class=\"l-list__item\">
<div class=\"c-checkbox\">
<input id=\"option_2\" type=\"checkbox\" name=\"checkbox\" class=\"c-checkbox__input\">
<label for=\"option_2\" class=\"c-checkbox__label\">Pears</label>
</div>
</li>
</ul>
<!-- .l-list -->
</div>
<!-- .c-card__body -->
</div>
<!-- .c-card -->
Это избавит вас от необходимости дублирования стилей и позволит использовать классы с меткой l в других частях страницы. Это вынудит нас писать немного больше кода, но это невысокая цена за читабельность, инкапсуляцию и возможность многократного использования.
9. Огромное количество классов у компонента
Многие утверждают, что довольно большое количество классов у элемента это не очень хорошо, к тому же картина осложняется модификаторами. Но если учесть, что правильное использование классов делает код более читабельным и упрощает поддержку, увеличение их числа не такая уж большая цена за преимущества этого подхода. Для примера давайте рассмотрим не самую красивую, но понятную разметку:
<button class=\"c-button c-button--primary c-button--huge is-active\">Click me!</button>
Однако если она вызывает у вас головную боль, вы можете воспользоваться методикой расширения, предложенной Сергеем Заровски. Она предлагает использовать .className [class^=\"className\"], [class*=\"className\"] для эмуляции расширенного функционала в ванильном CSS. С этой техникой предыдущая верстка будет выглядеть так:
<button class=\"c-button--primary-huge is-active\">Click me!</button>
10. Можем ли мы адаптивно менять тип компонента?
Примером этого может быть выпадающее меню, которое легко превращается в набор вкладок на экране мобильного устройства. Один компонент может иметь два абсолютно разных оформления, меняющихся в зависимости от размеров области просмотра.
В принципе, мы в обеих ситуациях мы можем обойтись c-компонентом, так как вне зависимости от размеров экрана, именно эту функцию выполняет меню. Но такое решение не совсем подходит для списка изображений, который превращается на большем экране в карусель. В этом случае будет вполне допустимо создать отдельный компонент для каждого случая.
Гарри Робертс предложил технику адаптивных суффиксов, которая прекрасно подойдет в этом случае, хотя и создавалась для несколько других ситуаций. Выглядит это приблизительно вот так:
<ul class=\"c-image-list@small-screen c-carousel@large-screen\">
Такой способ наименования поможет человеку, который будет разбираться в вашем коде, понять ваши намерения. Не обязательно использовать именно такие имена, но они хорошо передают суть решения.