BEM

BEM (Block-Element-Modifier) — методология именования CSS-классов, которая делает код предсказуемым, масштабируемым и лёгким для поддержки. Разработана в Яндексе.

Зачем нужно

Без системы именования CSS-классы быстро превращаются в хаос: .title, .title2, .big-title, .header-title. BEM даёт чёткие правила: каждый класс говорит, к какому компоненту (блоку) он относится, что он стилизует (элемент) и какой вариант описывает (модификатор).

Где используется

  • Крупные проекты с командной разработкой
  • Компонентный подход (каждый блок = компонент)
  • HTML-вёрстка без CSS-in-JS
  • Проекты с длительной поддержкой
  • Design-системы

Предпосылки

Концепция

Block (Блок)

Самостоятельный компонент, который имеет смысл сам по себе.

/* Блоки */
.card { }
.header { }
.search-form { }
.nav { }
.button { }

Element (Элемент)

Часть блока, которая не имеет смысла вне его контекста. Отделяется двойным подчёркиванием __.

/* Элементы блока .card */
.card__title { }
.card__image { }
.card__body { }
.card__footer { }

/* Элементы блока .search-form */
.search-form__input { }
.search-form__button { }

Modifier (Модификатор)

Вариант или состояние блока/элемента. Отделяется двойным дефисом --.

/* Модификаторы блока */
.card--featured { }
.card--compact { }
.button--primary { }
.button--large { }

/* Модификаторы элемента */
.card__title--highlighted { }
.nav__link--active { }

Примеры

Карточка

<article class="card card--featured">
  <img class="card__image" src="photo.jpg" alt="Фото">
  <div class="card__body">
    <h3 class="card__title">Заголовок</h3>
    <p class="card__text">Описание карточки</p>
  </div>
  <footer class="card__footer">
    <button class="button button--primary">Подробнее</button>
  </footer>
</article>
.card {
  border: 1px solid #dee2e6;
  border-radius: 12px;
  overflow: hidden;
}

.card--featured {
  border-color: #007bff;
  box-shadow: 0 4px 12px rgba(0, 123, 255, 0.15);
}

.card__image {
  width: 100%;
  height: 200px;
  object-fit: cover;
}

.card__body {
  padding: 16px;
}

.card__title {
  font-size: 1.25rem;
  margin-bottom: 8px;
}

.card__text {
  color: #6c757d;
  line-height: 1.6;
}

.card__footer {
  padding: 12px 16px;
  border-top: 1px solid #dee2e6;
}

Навигация

<nav class="nav">
  <a class="nav__link nav__link--active" href="/">Главная</a>
  <a class="nav__link" href="/about">О нас</a>
  <a class="nav__link" href="/contact">Контакты</a>
</nav>
.nav {
  display: flex;
  gap: 4px;
  background: #f8f9fa;
  padding: 4px;
  border-radius: 8px;
}

.nav__link {
  padding: 8px 16px;
  text-decoration: none;
  color: #333;
  border-radius: 6px;
  transition: background 200ms;
}

.nav__link:hover {
  background: #e9ecef;
}

.nav__link--active {
  background: #007bff;
  color: white;
}

Форма

<form class="form form--inline">
  <div class="form__group">
    <label class="form__label">Email</label>
    <input class="form__input form__input--error" type="email">
    <span class="form__message form__message--error">Невалидный email</span>
  </div>
  <button class="form__submit button button--primary">Отправить</button>
</form>
.form { }

.form--inline {
  display: flex;
  align-items: flex-end;
  gap: 16px;
}

.form__group {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.form__label {
  font-size: 0.875rem;
  font-weight: 600;
}

.form__input {
  padding: 8px 12px;
  border: 1px solid #ced4da;
  border-radius: 6px;
}

.form__input--error {
  border-color: #dc3545;
}

.form__message--error {
  color: #dc3545;
  font-size: 0.75rem;
}

Файловая структура

components/
├── card/
│   ├── card.css
│   └── card.html
├── button/
│   └── button.css
├── nav/
│   └── nav.css
└── form/
    └── form.css

Правила BEM

  1. Только классы — никаких id, тегов или вложенных селекторов
  2. Плоская структура.card__title, а не .card .title и не .card__body__title
  3. Элемент принадлежит блоку — нет __ внутри __:
    /* ОШИБКА */
    .card__body__title { }
    /* ПРАВИЛЬНО */
    .card__title { }
    
  4. Модификатор не используется без базового класса:
    <!-- ОШИБКА -->
    <div class="card--featured">
    <!-- ПРАВИЛЬНО -->
    <div class="card card--featured">
    
  5. Блок не зависит от контекста.card работает одинаково везде

Частые ошибки

  1. Глубокая вложенность элементовcard__header__title__icon:
    /* ОШИБКА — BEM не вкладывает элементы в элементы */
    .card__header__title { }
    /* ПРАВИЛЬНО — элемент всегда от блока */
    .card__title { }
    
  2. Использование тегов вместо классов:
    /* ОШИБКА — привязка к HTML-структуре */
    .card h3 { }
    /* ПРАВИЛЬНО */
    .card__title { }
    
  3. Модификатор без базового класса — модификатор только ДОБАВЛЯЕТ/ИЗМЕНЯЕТ стили
  4. Слишком много модификаторов — если модификаторов больше 3, возможно это отдельный блок
  5. BEM для утилитарных классов.mt-20, .text()-center — это не BEM, это утилиты

Практика

  • Создать компонент «карточка» по BEM
  • Сделать навигацию с active-модификатором
  • Создать форму с модификаторами ошибок
  • Организовать файловую структуру по блокам
  • Проверить: нет ли вложенных элементов (__ внутри __)

Связанные темы

Ресурсы