На протяжении многих лет я использовал BEM-подобный подход для написания CSS. Но я решил пойти другим путём — использовать семантические классы.
В итоге получилось гораздо меньше CSS, а писать его стало проще и предсказуемее.
Сегодня я хочу быстро рассказать, как это работает и почему я в восторге от такого подхода. Поехали!
/* Это Блок */
.button {
/* ... */
}Стили для дочерних элементов внутри блока — это элементы. Они используют паттерн именования .{block-class}__{child-element}.
Например, если у вас есть стили специально для иконок внутри .button, вы напишете:
/* Это Блок */
.button {
/* ... */
}
/* Это Элемент */
.button__icon {
/* ... */
}Модификаторы — это классы, которые изменяют стиль блока или элемента. Они используют паттерн .{block-or-element-class}—{modifier}.
Например, пусть у кнопки есть цвет по умолчанию, а также несколько других вариантов. И иконки тоже могут быть разных размеров.
/* Это Блок */
.button {
/* ... */
}
/* Это Элемент */
.button__icon {
/* ... */
}
/* Это Модификаторы */
.button--primary {
/* ... */
}
.button--secondary {
/* ... */
}
.button__icon--large {
/* ... */
}
.button__icon--small {
/* ... */
}Благословение и проклятие BEM
Цель системы вроде BEM — избежать конфликтов, которые возникают при использовании одинаковых имён классов в разных контекстах, и сделать очевидным назначение каждого класса.
И это действительно работает.
Но… это также приводит к огромному количеству дублирующегося CSS.
Возьмём, к примеру, .button—primary. Он, скорее всего:
- задаёт background и border в цвет var(—primary),
- меняет цвет текста на контрастный,
- добавляет более тёмный оттенок при :hover и :active.
А если у вас есть .callout, .badge или .tabs, которым нужны те же цвета? Вам придётся дублировать эти стили для каждого компонента:
.callout {
/* ... */
}
.callout--primary {
/* ... */
}
.callout--secondary {
/* ... */
}Это нарушает принцип DRY (Don’t Repeat Yourself — «не повторяйся»). Именно так библиотеки вроде Bootstrap становятся такими большими.
А писать такой HTML — ещё и кошмар.
<div class="callout callout--secondary"> <h2>Купите это прямо сейчас!</h2> <p>Сделайте это сейчас!</p> <button class="button button--primary"> Ok! <span class="icon icon--large">...</span> </button> </div>
Все эти длиннющие имена классов — сплошная морока.
Есть способ проще.
Семантические классы
Альтернативный подход — использовать семантические классы.
Вместо того чтобы писать модификаторы для каждого блока или элемента, вы создаёте общие семантические классы, которые можно применять к любому элементу.
.primary {
background-color: var(--primary);
border-color: var(--primary);
color: var(--on-primary);
}
.secondary {
background-color: var(--secondary);
border-color: var(--secondary);
color: var(--on-secondary);
}
.size-large {
font-size: 1.2em;
}
.size-small {
font-size: 0.8em;
}Базовые стили компонентов всё ещё остаются:
.button {
/* ... */
}
.icon {
/* ... */
}
.callout {
/* ... */
}
.badge {
/* ... */
}
.tabs {
/* ... */
}Но теперь они комбинируются как Lego-блоки.
HTML становится чище и проще:
<div class="callout secondary"> <h2>Купите это прямо сейчас!</h2> <p>Сделайте это сейчас!</p> <button class="button primary"> Ok! <span class="icon size-large">...</span> </button> </div>
Когда я начал работать над Kelp, я сначала использовал BEM. Но очень быстро перешёл на семантические классы — и размер CSS-файла резко уменьшился.
Это не «лучше», а просто другой подход
Я поставил слово «лучше» в кавычки, потому что это не объективно лучший метод. Это другой способ, с другими целями и компромиссами.
Мне он подходит лучше, потому что он:
- сокращает объём кода;
- делает разметку проще;
- ускоряет разработку.
У вас могут быть другие задачи — и это тоже нормально.
Вольный перевод статьи.