Вёрстка сайта по методологии BEM
BEM (Block, Element, Modifier) — методология именования CSS-классов и организации компонентов, разработанная в Яндексе. В крупных проектах BEM решает конкретную проблему: CSS глобален, и без системы именования стили из одной части сайта ломают другую.
Синтаксис
.block {} /* Блок — независимый компонент */
.block__element {} /* Элемент — часть блока, не существующая отдельно */
.block--modifier {} /* Модификатор — состояние или вариация блока */
.block__element--mod {} /* Модификатор элемента */
Двойное подчёркивание — элемент. Двойное тире — модификатор. Никаких трёх уровней вложенности в классе (block__element__subelement — ошибка).
Практический пример: карточка товара
<article class="product-card product-card--featured">
<div class="product-card__badge product-card__badge--new">Новинка</div>
<figure class="product-card__media">
<img
class="product-card__image"
src="product.webp"
alt="Название товара"
width="320"
height="240"
>
</figure>
<div class="product-card__body">
<h2 class="product-card__title">Название товара</h2>
<p class="product-card__description">Краткое описание...</p>
<div class="product-card__pricing">
<span class="product-card__price product-card__price--current">2 990 ₽</span>
<span class="product-card__price product-card__price--old">4 490 ₽</span>
<span class="product-card__discount">−33%</span>
</div>
</div>
<footer class="product-card__footer">
<button class="btn btn--primary btn--full-width product-card__action">
В корзину
</button>
<button class="wishlist-btn product-card__wishlist" aria-label="В избранное">
<svg class="wishlist-btn__icon">...</svg>
</button>
</footer>
</article>
CSS:
/* Блок */
.product-card {
display: grid;
grid-template-rows: auto 1fr auto;
border: 1px solid var(--color-border);
border-radius: 8px;
overflow: hidden;
background: var(--color-surface);
transition: box-shadow 200ms ease;
}
.product-card:hover {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
}
/* Модификатор блока */
.product-card--featured {
border-color: var(--color-accent);
}
/* Элементы */
.product-card__media {
position: relative;
aspect-ratio: 4 / 3;
overflow: hidden;
margin: 0;
}
.product-card__image {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 300ms ease;
}
.product-card:hover .product-card__image {
transform: scale(1.05);
}
.product-card__badge {
position: absolute;
top: 12px;
left: 12px;
padding: 2px 8px;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 600;
}
.product-card__badge--new { background: #22c55e; color: white; }
.product-card__badge--sale { background: #ef4444; color: white; }
.product-card__body {
padding: 16px;
display: flex;
flex-direction: column;
gap: 8px;
}
.product-card__pricing {
display: flex;
align-items: baseline;
gap: 8px;
}
.product-card__price--current {
font-size: 1.25rem;
font-weight: 700;
color: var(--color-text);
}
.product-card__price--old {
font-size: 0.875rem;
color: var(--color-muted);
text-decoration: line-through;
}
.product-card__footer {
padding: 12px 16px;
display: flex;
gap: 8px;
border-top: 1px solid var(--color-border);
}
.product-card__action { flex: 1; }
Структура файлов (File-per-block)
src/
blocks/
product-card/
product-card.html # Шаблон / Storybook
product-card.css # Стили блока
product-card.js # JS блока (опционально)
btn/
btn.css
wishlist-btn/
wishlist-btn.css
header/
header.css
header.js
Один файл на блок — чёткое разграничение. В сборке (Vite, Webpack) импортируем только нужные блоки.
Отличия от CSS Modules и utility-first
| Подход | Сильные стороны | Слабые стороны |
|---|---|---|
| BEM | Читаемые классы, явная структура, не требует тулинга | Многословность, риск «а что это вообще за элемент» |
| CSS Modules | Автоматическая изоляция, нет конфликтов имён | Требует сборщик, сложнее debug в DevTools |
| Tailwind | Скорость прототипирования, нет именования | Длинные className, трудно читать HTML |
BEM остаётся актуальным выбором для:
- Команд без единого JS-фреймворка (MPA, CMS-шаблоны)
- Проектов, где дизайнеры или верстальщики работают с чистым HTML/CSS
- Стилегайдов и UI-библиотек, которые используются в разных контекстах
Типичные ошибки
Три уровня вложенности в классе:
<!-- Неправильно -->
<div class="nav__list__item">
<!-- Правильно — item является элементом блока nav, не list -->
<div class="nav__item">
Модификатор без базового класса:
<!-- Неправильно -->
<button class="btn--primary">
<!-- Правильно -->
<button class="btn btn--primary">
Контекстные стили через вложенность:
/* Неправильно — привязка к контексту, нарушает изолированность */
.sidebar .product-card { font-size: 0.875rem; }
/* Правильно — модификатор -->
.product-card--compact { font-size: 0.875rem; }
Глобальные стили состояний вместо модификаторов:
/* Неправильно */
.is-active { color: red; }
/* Правильно */
.nav__link--active { color: var(--color-accent); }
Сроки
Вёрстка по BEM требует чуть больше времени из-за продуманного именования, но экономит время при поддержке:
| Объём | Время |
|---|---|
| Landing page (6–8 блоков) | 1.5–2.5 дня |
| Корпоративный сайт | 4–6 дней |
| UI-кит (20–30 компонентов) | 5–8 дней |







