36 Модуль 8 Архитектура стилей 16 мин урок 11 из 25

Именование: БЭМ и что с ним не так

Имена ломаются раньше кода. Разбираем БЭМ по-человечески, где он избыточен и как называть то, у чего названия нет.

Что ты научишься делать

  • Назовёшь части интерфейса так, чтобы имена не устарели через месяц
  • Поймёшь, где БЭМ помогает, а где превращается в ритуал
  • Отделишь состояние от варианта — это разные вещи

#Имена ломаются первыми

Код живёт годами и переживает несколько редизайнов. Имена классов переживают их плохо.

Класс .blue-button становится враньём в тот день, когда кнопку просят сделать зелёной. Класс .left-column — когда колонку переносят вправо. .header-2 не был правдой изначально.

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

Так не надо

<div class="red-box big">
  <p class="p14 mt10">Заказ не прошёл</p>
</div>

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

Так надо

<div class="alert alert--error">
  <p class="alert__text">Заказ не прошёл</p>
</div>

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

#БЭМ за две минуты

Блок — самостоятельная штука, которую можно перенести в другое место и она останется собой: card, menu, alert.

Элемент — часть блока, отдельно не имеющая смысла. Пишется через два подчёркивания: card__title.

Модификатор — вариант блока или элемента. Через два дефиса: card--sale, alert--error.

Странные разделители нужны, чтобы отличать границу блока от границы слова: product-card__title--big читается однозначно.

Интерактивный разбор доступен при включённом JavaScript.

#Что с ним не так

БЭМ придумали для больших проектов с несколькими командами. В маленьких он часто превращается в ритуал.

  • Длина. product-card__price-wrapper--discounted — реальное имя из живого проекта. Читать такое тяжело.
  • Ложная вложенность. Соблазн писать card__body__title, хотя элемент всегда принадлежит блоку, а не другому элементу. Правильно — card__title, сколько бы уровней разметки между ними ни было.
  • Элемент ради элемента. Если внутри блока один заголовок и он всегда выглядит одинаково, можно обойтись .card h3. Это не преступление.
  • Модификатор вместо состояния. Об этом ниже — путаница здесь самая частая.

#Вариант и состояние — разные вещи

Модификатор — постоянное свойство: эта карточка со скидкой, эта кнопка второстепенная. Задаётся при вёрстке и не меняется.

Состояние — временное: сейчас открыто, сейчас загружается, сейчас ошибка. Меняется во время работы.

Их принято разделять по написанию: модификатор через --, состояние через приставку is- или has-.

HTMLРазница на практике
<!-- Вариант задан один раз при вёрстке --><button class="btn btn--ghost">Отмена</button><!-- Состояние появляется и исчезает во время работы --><button class="btn btn--primary is-loading" disabled>  Сохраняем…</button><!-- Ещё честнее: состояние атрибутом, а не классом --><button class="btn btn--primary" data-state="loading" disabled>  Сохраняем…</button>
Варианты и состояния рядом
В песочницу

#Как назвать то, у чего нет названия

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

Работающие подходы, по порядку предпочтения.

  1. Спроси, как это называет заказчик

    Если в разговоре звучит «плашка с акцией» — значит это promo, а не yellow-block. Язык предметной области почти всегда точнее выдуманного.

  2. Назови по задаче, а не по виду

    Не gray-strip, а divider. Не big-text, а lead. Задача переживёт редизайн, вид — нет.

  3. Для обёрток бери честные служебные имена

    container, stack, cluster, grid — нормальные имена для блоков, у которых единственная роль в раскладке. Врать они не начнут.

  4. Если не придумывается — возможно, блок лишний

    Невозможность назвать часто означает, что этот div ничего не делает. Попробуй убрать его: иногда раскладка не замечает пропажи.

#Альтернативы БЭМ

БЭМ решает одну задачу: изоляцию стилей в проекте, где нет сборки. Если сборка есть, задачу решают иначе.

ПодходКак изолируетЦена
БЭМСоглашением об именахДлинные имена, дисциплина держится на людях
CSS-модулиСборщик дописывает хеш к каждому классуНужна сборка; в инструментах разработчика имена нечитаемы
УтилитыИзолировать нечего: классы атомарныеРазметка перестаёт читаться
data-атрибутыСелектор по атрибуту вместо классаСелекторы длиннее, специфичность как у класса

Ни один из них не «лучше». В проекте без сборки, как этот учебник, БЭМ остаётся самым разумным: он не требует ничего, кроме договорённости.

#Как это выглядит в реальном проекте

CSSproduct-card.css
@layer components {  /* Блок. Ни слова о том, где он лежит на странице */  .product {    display: grid;    gap: 8px;    padding: 16px;    background: var(--color-surface);    border-radius: 14px;  }  /* Элементы — плоские селекторы, вес 0-1-0 */  .product__title { margin: 0; font-size: 1rem; }  .product__price { font-weight: 700; }  /* Вариант: задаётся при вёрстке */  .product--compact { padding: 10px; gap: 4px; }  .product--compact .product__title { font-size: .9rem; }  /* Состояние: приходит и уходит */  .product[data-state="out-of-stock"] .product__price {    color: var(--color-muted);    text-decoration: line-through;  }  /* Раскладка не в компоненте: у карточки нет внешних отступов.     Расстояния задаёт тот, кто её размещает */  .catalog { display: grid; gap: 20px; }}

#Когда НЕ надо

  • БЭМ на лендинге в один экран. Пять блоков, которые никто не будет переиспользовать. Длинные имена тут не окупаются.
  • БЭМ поверх фреймворка компонентов. Если стили и так изолированы на уровне сборки, префиксы дублируют то, что уже сделано.
  • Элементы для всего подряд. Блок с восемнадцатью элементами — это, скорее всего, три блока, которые слиплись.
  • Переименовывать работающий код целиком. Смена схемы именования — большая правка с риском. Вводи новую схему в новом коде, старую трогай только когда всё равно её редактируешь.

Где спотыкаются почти все

Имя описывает внешний вид

Почему Через редизайн .blue-btn становится зелёной. Имя врёт, но переименовать страшно: непонятно, где ещё используется.

Как надо Называй по роли: .btn--primary, .alert--error.

Двойная вложенность в элементе

Почему card__body__title привязывает элемент к структуре разметки. Переставишь заголовок на уровень выше — имя станет ложью.

Как надо Элемент принадлежит блоку, а не другому элементу: card__title.

Состояние оформлено как модификатор

Почему .modal--open выглядит как вариант модального окна, хотя это временное состояние. При чтении кода непонятно, кто и когда его ставит.

Как надо Состояния — через is- или data-state.

У компонента есть внешний margin

Почему В новом месте отступ оказывается лишним, и появляется card--flush, потом card--flush-top.

Как надо Отступы задаёт контейнер, а не компонент.

Блок block
Самостоятельная часть интерфейса, переносимая в другое место без изменений.
Элемент element
Часть блока, не имеющая смысла отдельно от него. Пишется через __.
Модификатор modifier
Постоянный вариант блока или элемента. Пишется через --.
Состояние state
Временное свойство: открыто, загружается, ошибка. Пишется через is- или атрибутом.
Плоский селектор flat selector
Селектор из одного класса. Легко перекрывается и не задевает чужие блоки.

Проверь себя

  1. Почему <code>.blue-button</code> — плохое имя?

    Имя должно называть роль. Цвет — свойство оформления, и он меняется чаще всего.

  2. Как правильно назвать заголовок внутри тела карточки?

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

  3. Что из этого — состояние, а не модификатор?

    Загрузка временна: началась и закончилась. Вариант оформления задаётся один раз при вёрстке.

  4. В чём главная практическая польза БЭМ?

    Один класс вместо цепочки — правило легко перекрыть и невозможно случайно задеть чужой блок. Кода при этом обычно больше.

  5. Почему компоненту не задают внешний margin?

    Расстояния — забота контейнера. Иначе один компонент требует правок под каждое место размещения.

Задание

Переименовать компонент

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

Переименуй классы по БЭМ: блок alert, элементы через __, вариант через --, состояние через is-. Убери внешний margin у блока — отступ должен задавать контейнер .stack.

Открыть в песочнице
    Подсказка 1

    Блок — alert. Элементы принадлежат блоку напрямую: alert__text, alert__btn. Никакого __body__ посередине.

    Подсказка 2

    Состояние: <div class="alert is-muted"> и правило .alert.is-muted { opacity: .5; }

    Подсказка 3

    Убери margin-bottom у блока и добавь .stack { display: grid; gap: 16px; }

    Показать решение
    HTMLindex.html
    <div class="stack">  <div class="alert is-muted">    <p class="alert__text">Заказ не прошёл</p>    <button class="alert__btn">Повторить</button>  </div></div>
    CSSstyle.css
    .stack {  display: grid;  gap: 16px;}.alert {  padding: 14px;  background: #fdecee;  border-left: 3px solid #c8394a;  border-radius: 10px;}.alert__text { margin: 0 0 10px; color: #c8394a; }.alert__btn {  padding: 8px 14px;  font: inherit;  color: #fff;  background: #c8394a;  border: 0;  border-radius: 999px;}.alert.is-muted { opacity: .5; }

    Проверь себя