
Соглашения по именованию CSS позволяют писать строгий, чистый и красивый код. При соблюдении правил именования вы всегда будете знать:
- Для чего используется класс;
- Где класс может быть использован;
- С какими другими классами связан этот класс.
Правила именования, которым я следую, весьма просты: я использую дефис в качестве разделителя, а в сложных местах использую БЭМ-подобное именование.
Следует отметить, что сами по себе правила именования не дадут особой выгоды при написании CSS; но зато они весьма полезны при просмотре разметки.
Разделение дефисом
Все слова в названиях классов должны быть разделены дефисом:
.page-head {}
.sub-content {}
CamelCase и знак подчеркивания не используются для классов, следующий пример неправилен:
.pageHead {}
.sub_content {}
БЭМ-подобное именование
Для более крупных взаимосвязанных частей интерфейса я использую БЭМ-подобное именование классов.
БЭМ, то есть Блок, Элемент, Модификатор, это методология, созданная разработчиками Яндекса. Несмотря на то, что БЭМ — это довольно крупная методология, в данный момент мы заинтересованы только в ее способе именования элементов. Причем, мое соглашение по именованию немного отличается от оригинального БЭМ'a: принципы одни и те же, но синтаксис разный.
БЭМ разделяет компоненты верстки на три группы:
- Блок: главный корневой элемент.
- Элемент: часть блока.
- Модификатор: вариант или модификация блока.
Проведем аналогию:
.person {}
.person__head {}
.person--tall {}
В начале класса всегда ставится название блока, для обозначения элемента мы отделяем название блока от названия элемента двумя подчеркиваниями (__), а для обозначения модификатора используем два дефиса (--).
В примере выше мы можем видеть, что
.person {}
— это блок; у него нет предков. .person__head {}
— это элемент, часть блока; наконец, .person--tall
— это модификатор, разновидность блока .person {}
.Использование блоков
Блок должен быть логической, самостоятельной единицей. Продолжая наш пример с классом
.person {}
: мы не можем создать класс .room__person
, потому что .room {}
— это самостоятельная единица. В таком случае стоит разделять блоки:.room {}
.room__door {}
.room--kitchen {}
.person {}
.person__head {}
Если нам потребуется обозначить человека внутри комнаты, было бы правильнее использовать такой селектор —
.room .person
, который позволяет не городить кашу из кучи разных непонятных элементов и блоков.Более реалистичный пример правильного использования блоков может выглядеть следующим образом:
.page {}
.content {}
.sub-content {}
.footer {}
.footer__copyright {}
Каждая часть кода представляет свой собственный блок. Неправильный пример использования:
.page {}
.page__content {}
.page__sub-content {}
.page__footer {}
.page__copyright {}
Важно уметь различать, где стоит применять БЭМ, а где нет. Как правило, я использую блоки для описания автономных частей пользовательского интерфейса.
Множество слоев
Если бы мы добавили к нашему блоку
.person {}
еще один элемент, скажем, .person__eye
, то нам не нужно было бы при именовании элемента делать шаг назад, добавляя названия предыдущих элементов, вплоть до корневого элемента. То есть, правильно будет писать .person__eye
, а не .person__head__eye
.Добавляем модификации элементов
Вам может потребоваться добавлять вариации элементов, это может быть сделано несколькими способами, в зависимости от того, как и почему эти элементы должны быть изменены. Опять же, если человек имеет голубые глаза, то в CSS это может быть описано так:
.person__eye--blue {}
Однако, в реальных проектах все бывает несколько сложнее. Прощу прощения за такую аналогию, но давайте себе представим человека с красивым лицом. Сам по себе он не особо красив, поэтому лучшим решением будет добавить модификатор к элементу
.person__face {}
:.person__face--handsome {}
Но что делать, если мы хотим описать лицо красивого человека? То есть человек красив сам по себе, в отличие от предыдущего примера, и нам нужно описать его лицо? Это делается следующим образом:
.person--handsome .person__face {}
Это один из немногих случаев, когда мы можем менять элемент в зависимости от модификации блока. При использовании Sass получился бы такой код:
.person {}
.person__face {
.person--handsome & {}
}
.person--handsome {}
Заметьте, что мы не добавляем новый элемент
.person__face
внутрь элемента .person--handsome
; вместо этого мы используем родительский селектор Sass внутри уже существующего селектора .person__face
. Это значит, что все правила, связанные с .person__face
будут находиться в одном месте, и нам не придется разбрасывать их по всему файлу. Это хорошая практика при работе с вложенным кодом: храните все нужные стили внутри одного контекста (в нашем случае, внутри .person__face
).Именование в разметке
Как ранее было замечено, соглашение по именованию классов наиболее полезно при работе с разметкой. Взгляните на следующий кусок разметки, не следующий нашему соглашению:
<div class="box profile pro-user">
<img class="avatar image" />
<p class="bio">...</p>
</div>
Как классы
.box
и .profile
связаны друг с другом? Как классы .profile
и .avatar
связаны друг с другом? Связаны ли они вообще? Зависит ли класс .bio
от класса .pro-user
? Можно ли использовать класс .avatar
вне этой разметки?При просмотре такой разметки очень сложно ответить на все эти вопросы. Использование соглашения об именовании меняет дело:
<div class="box profile profile--is-pro-user">
<img class="avatar profile__image" />
<p class="profile__bio">...</p>
</div>
Теперь нам сразу видно, какие классы связаны друг с другом и как, а какие нет; мы знаем, какие классы мы не можем использовать вне этой разметки; наконец, мы знаем, какие классы могут быть использованы в любом другом месте.
JavaScript-хуки
Как правило, неразумно привязывать JS- и CSS-код к одному и тому же классу в разметке, потому что удалив или изменив один класс с целью, например, изменения поведения скрипта, вы непременно затронете CSS, и наоборот. Намного чище, прозрачнее и в целом лучше привязывать JS к отдельным классам.
Я сталкивался со случаями, когда удаление каких-то классов с целью переработки стилей, ломало работу всех скриптов на странице, а все потому что разработчик не подумал и привязал стили со скриптами к одному и тому же классу.
Как правило, разработчики используют отдельный класс для js, начинающийся с префикса «js-», например:
<input type="submit" class="btn js-btn" value="Follow" />
Такая разметка позволяет использовать стили
.btn
в любом другом месте, при этом не затрагивая поведения .js-btn
.data-* атрибуты
Также довольно часто разработчиками используются data-* атрибуты в качестве js-хуков, но это неправильно. data-* атрибуты, согласно спецификации, предназначены для хранения данных, недоступных на странице. data-* атрибуты созданы для хранения данных, а не для для привязки к js.
В продолжение темы...
Как уже было сказано, все правила, представленные выше весьма просты. Я призываю вас не останавливаться на изученном и читать другие материалы по этой теме — это позволит вам получить больше возможностей по именованию классов.
Материалы для дополнительного изучения
Предыдущая часть: CSS GuideLines, часть 2. Комментирование кода