← Блог · · Команда ConvertCaseHQ · 6 мин чтения

Именование классов в CSS: kebab-case, БЭМ и не только

Большинство имён классов в CSS пишут строчными буквами в kebab-case (.site-header, .price-tag), потому что так CSS называет и свои собственные сущности: background-color, font-size, flex-direction. БЭМ (BEM) добавляет к этому жёсткую структуру, block__element--modifier, так что по классу вроде card__title--large видно, к какому компоненту он относится, какая это часть компонента и какой вариант она показывает.

Ниже разберём, что в CSS чувствительно к регистру, а что нет, почему kebab-case стал стандартом, как на самом деле звучат правила именования БЭМ и когда другой подход делает БЭМ ненужным.

Что в CSS чувствительно к регистру, а что нет

Собственный синтаксис CSS не чувствителен к регистру в пределах ASCII, поэтому COLOR: RED работает точно так же, как color: red. Иначе устроены три вещи:

  • Имена классов чувствительны к регистру. .Card не совпадёт с class="card". Единственное исключение — страница в режиме совместимости (quirks mode), где браузеры сравнивают имена классов без учёта регистра; это ещё одна причина начинать каждую страницу с <!DOCTYPE html>.
  • Пользовательские свойства чувствительны к регистру. --brand-color и --Brand-Color — две разные переменные.
  • Имена HTML-элементов и атрибутов — нет. В HTML-документе DIV и div выбирают одни и те же элементы. В XML они бы различались.

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

Почему в именах классов CSS победил kebab-case

Ничто в CSS не обязывает писать имена классов в kebab-case. .cardTitle и .card_title тоже допустимы. Дефисы логичны потому, что всё вокруг имени класса уже их использует: имена свойств, значения вроде space-between и HTML-атрибуты, такие как aria-label и data-user-id. Класс в camelCase посреди таблицы стилей с дефисами выглядит как опечатка.

Неудобства у kebab-case начинаются в JavaScript, потому что дефисы в идентификаторах запрещены. Свои случаи браузер обрабатывает сам: data-user-id читается как element.dataset.userId, а background-color превращается в element.style.backgroundColor. Имена классов обычно остаются строками (classList.add("is-open")), так что им это не мешает.

Исключение — CSS Modules. CSS-модуль экспортирует имена классов в JavaScript в виде объекта, поэтому .card-title приходится писать как styles["card-title"], а .cardTitle превращается в более аккуратное styles.cardTitle. Это единственная ситуация, в которой мы бы перешли на имена классов в camelCase.

Именование по БЭМ: блок, элемент, модификатор

БЭМ (Блок, Элемент, Модификатор; по-английски BEM) появился в Яндексе. Страница об истории методологии возводит эту работу к 2005 году, называет началом БЭМ выпуск фреймворка Яндекса Lego 2.0 в марте 2009 года, а переход к открытому исходному коду относит к 2010 году. Правила именования короткие:

  • Блок: самостоятельный компонент, который имеет смысл сам по себе, например .card, .search-form или .menu.
  • Элемент: часть блока, которая не имеет смысла вне его; имя строится так: блок, два подчёркивания, элемент — .card__title, .menu__item.
  • Модификатор: флаг, который меняет внешний вид, поведение или состояние; имя строится так: блок или элемент, два дефиса, модификатор — .card--featured, .card__button--disabled. Модификатор может быть и парой «ключ — значение»: .form--theme-xmas.

Слова внутри каждой части разделяются одиночными дефисами, как в .search-form__submit-button. Вот компонент целиком:

<article class="card card--featured">
  <h2 class="card__title">Spring sale</h2>
  <p class="card__body">Everything 20% off this week.</p>
  <a class="card__button card__button--disabled" href="/sale">Shop now</a>
</article>
.card { border: 1px solid #ddd; padding: 1rem; }
.card--featured { border-color: gold; }
.card__title { font-size: 1.25rem; }
.card__button--disabled { opacity: 0.5; pointer-events: none; }

Классический БЭМ и стиль с двумя дефисами

Если заглянуть на официальный сайт методологии, bem.info, модификаторы там выглядят иначе. В классической схеме модификатор отделяется одним подчёркиванием: menu_hidden, menu__item_visible, menu__item_type_radio. Вариант с -- указан там как альтернативный, «Two Dashes style» (стиль «два дефиса»), и именно его описывает getbem.com. Оба варианта — полноценный БЭМ; главное, не смешивайте их в одной кодовой базе. bem.info упоминает и небольшой подвох с дефисами: двойной дефис внутри HTML-комментария может не пройти валидацию.

Правила БЭМ, в которых легко ошибиться

  1. Никаких элементов элементов. Заголовок внутри шапки карточки — это всё равно card__title, а не card__header__title. В HTML элементы можно вкладывать сколь угодно глубоко, но имена остаются плоскими. Довод bem.info: с цепочками в именах нельзя переместить, добавить или удалить элемент, не меняя существующий код.
  2. Модификатор никогда не стоит один. Пишите class="button button--primary", а не class="button--primary". Модификатор лишь дополняет базовый класс.
  3. Используйте селекторы из одного класса. .card__title, а не .card .card__title или h2.card__title. Общепринятое исключение — модификатор блока, который меняет его элементы: .card--featured .card__title.
  4. Называйте модификаторы по назначению. Это наше мнение, а не правило БЭМ: --featured или --error переживут редизайн, а --yellow станет неправдой в тот день, когда сменится фирменный цвет.
  5. Не цепляйте JavaScript к классам для стилей. Это тоже наше мнение: в скриптах выбирайте элементы по data-атрибуту вроде data-action="close", а не по .card__button, тогда переименование класса ради оформления не сломает поведение.

Переименовать блок позже — по большей части задача для поиска и замены. Вставьте шаблон в наш инструмент «Найти и заменить», включите режим «Регулярка» и замените card(__|--) на product-card$1: все классы элементов и модификаторов перейдут на новое имя блока, а счётчик совпадений покажет, сколько их изменилось. Сам класс card переименуйте отдельно, чтобы не зацепить посторонние слова.

БЭМ в Sass и во встроенной вложенности CSS

Родительский селектор Sass умеет добавлять суффикс к внешнему селектору, и в документации Sass в качестве примера приводится именно БЭМ. Встроенная вложенность CSS так не умеет, и MDN прямо об этом предупреждает: вложенный &__title читается как селектор типа, поэтому .card__title из него не получится.

// Sass: works
.card {
  &__title { font-size: 1.25rem; }    // compiles to .card__title
  &--featured { border-color: gold; } // compiles to .card--featured
}
/* Native CSS nesting: write the full class names */
.card {
  border: 1px solid #ddd;
  &.card--featured { border-color: gold; } /* .card.card--featured */
}
.card__title { font-size: 1.25rem; }

За пределами БЭМ: когда он не нужен

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

  • CSS Modules по умолчанию делают имена классов локальными, поэтому простой .title в одном компоненте не столкнётся с .title в другом.
  • Utility-first CSS, например Tailwind, в основном заменяет собственные имена классов готовыми утилитами вроде font-bold, так что именование переезжает в ваши компоненты.
  • Небольшим сайтам с несколькими компонентами часто хватает простого kebab-case и префикса для каждого компонента.

Но и тогда kebab-case остаётся надёжным выбором по умолчанию для любого имени класса, которое вы всё-таки пишете.

Практические правила

  • Строчный kebab-case для имён классов и пользовательских свойств, всегда.
  • БЭМ, когда одной глобальной таблицей стилей пользуется много людей: block__element--modifier, плоские имена элементов, модификаторы рядом с базовым классом.
  • Имена классов в camelCase, только если инструмент вроде CSS Modules передаёт их в JavaScript.

Нужно превратить название компонента в класс? Введите «Product Card» в наш конвертер регистра, и кнопка kebab-case выдаст product-card. Соглашения для всех остальных слоёв проекта, от переменных окружения до URL, собраны в нашей шпаргалке по стилям именования для разработчиков.