Структурування селекторів класів CSS за допомогою Sass

2

Від автора: існує маса способів іменування CSS. Ви, напевно, чули про BEM і SMACSS (останній більше ніж просто спосіб). Також є OOCSS, більше схожий на цілу методику. Всі вони в значній мірі залежать від селекторів класів CSS із-за їх великої універсальності.

Sass допомагає писати селектори класів в модульній формі. За допомогою спадкування селекторів і mixin’ов ми можемо вигадувати божевільні API. У цій статті я продемонструю (ще раз) пару цих способів і перерахую їх плюси і мінуси, з моєї точки зору.

Нативне спадкування селекторів

Можливість успадковувати селектори, не повторюючи оригінальні імена блоків, давно напрошувалася в Sass. І у версії 3.3 ця функція, нарешті, була представлена. У бета-версії синтаксис був дуже дивним, пізніше з випуском стабільної версії він був поліпшений. У своїй статті Natalie Weizenbaum пояснила причини зміни синтаксису.

В основному селектор-посилання (&) може використовуватися як частина імені підкласу для створення іншого класу від першого батька на кореневому рівні документа (тобто @at-root не потрібен).

.foo {
// Стилі `.foo`
&-bar {
// Стилі `.foo-bar`
}
}

Незабаром цією функцією будуть зловживати при написанні BEM селекторів, як у вкрай популярний медіа-об’єкті:

.media {
// Стилі для блоку `.media`
&__img {
// Стилі для елемента `.media__image`
&—full {
// Стилі для модифікованого елемента`.media__image—full`
}
}
&—new {
// Стилі модифікатора`.media—new`
}
}

Код вище буде складати:

.media {}
.media__img {}
.media__img—full {}
.media—new {}

Плюси

Використовуються нативні функції. Не потрібно жодних додаткових помічників, таких як змінні або mixin’и.

В цілому досить легко розібратися в роботі селекторів-посилань (&).

Мінуси

Синтаксис (&) здається трохи заплутаним, якщо не страшним для розробників, не знайомих з Sass.

Якщо не використаний @at-root, то спадкування зазвичай не распрастраняется на кореневий рівень, що може збентежити.

BEM mixins

З-за вкрай незручного синтаксису генерації класів в бета-версії Sass 3.3 (at-root #{&}__element) для створення більш дружніх API то тут, то там почали з’являтися mixin’и, як заміна потворного синтаксису.

@mixin element($element) {
&__#{$element} {
@content;
}
}
@mixin modifier($modifier) {
&—#{$modifier} {
@content;
}
}

Ви використовуєте їх так:

.media {
// Стилі `.media` block
@include element(«image») {
// Стилі для елемента `.media__image` element
@include modifier(«full») {
// Стилі для модифікованого елемента `.media__image—full`
}
}
@include modifier(«new») {
// Стилі модифікатора `.media—new`
}
}

За таким же принципом ми могли б створити блок mixin, але від нього не буде користі, якщо блок не відповідає одному імені класу. Давайте спростимо. Так як декому хочеться набирати імена модифікаторів і елементів, ми згадали пару скорочень e та m.

.media {
// Стилі для блоку`.media`
@include e(«image») {
// Стилі для елемента `.media__image`
@include m(«full») {
// Стилі для модифікованого елемента `.media__image—full`
}
}
@include m(«new») {
// Стилі модифікатора `.media—new`
}
}

Плюси

Якщо ви знаєте, як працює BEM, то вам не складе труднощів розібратися в API цієї версії.

Мінуси

Вся логіка прихована під mixin’ами, і якщо ви не зовсім розумієте, як це працює, то зовсім не факт, що нові класи і селектори будуть сгенерированны.

Mixin’и в одну букву швидше за все не самий вдалий варіант, іноді складно зрозуміти призначення mixin’а. B і m можуть означати все, що завгодно.

Очеловеченные-BEM mixin’и

Нещодавно я прочитав про новий BEM-подібному методі у статті Anders Schmidt Hansen. Основна ідея статті полягає в тому, щоб заховати жаргон «Блок-Елемент-Модифікатор» за загальними поняттями, які і так зрозумілі.

@mixin new($block) {
@at-корінь .#{$block} {
@content;
}
}
@mixin has($element) {
&__#{$element} {
@content;
}
}
@mixin when($modifier) {
&—#{$modifier} {
@content;
}
}

У цьому випадку вся суть у тому, щоб заховати код за продуманим mixin’ами таким чином, щоб це виглядало як код нового mixin’а.

@include new(«media») {
// Стилі для блоку `.media`
@include has(«image») {
// Стилі для елемента `.media__image`
@include when(«full») {
// Стилі для модифікованого елемента `.media__image—full`
}
}
@include when(«new») {
// Стилі модифікатора `.media—new`
}
}

Anders пішов ще далі з is(..) і holds(..) mixin’ами. Вся ця ідея частково нагадує мені мій when-inside(..) mixin, що приховує & за доброзичливим mixin’ом під час стилізації елемента, заснованого на даному контексті.

@mixin when-inside($selector) {
#{$selector} & {
@content;
}
}
img {
display: block;
@include when-inside(«.media-inline») {
display: inline;
}
}

Плюси

Даний метод робить код більш читабельним. Наприклад, коли ми почали називати наші класи станів з приставкою is- (стало популярним в SMACSS).

До цих пір цей підхід дотримується певної методології (у нашому випадку BEM). Однак, код стає набагато дружелюбніше.

Мінуси

Більше mixin’ів, більше помічників, необхідно ще більше все вивчити, щоб у всьому розібратися. Ніхто не любить писати тонни mixin’ів заради простих CSS селекторів.

Для деяких це може бути занадто абстрактно; не всім подобається читати код по-англійськи. Залежить від знання мови.

Прикінцеві міркування

Пам’ятайте, що дані методи убезпечать вас від пошуку вихідного селектора. І вже тим більше, якщо він зовсім не існує, а генерується Sass. Коментарі перед селекторами вирішують цю проблему, але чому б просто не писати селектори безпосередньо в одному місці?

У будь-якому випадку, це найпопулярніші способи написання CSS селекторів з допомогою Sass, які я знаю. І між нами: жоден мені не подобається. І не тільки із-за проблеми з пошуком, для мене це не така вже й проблема.

Я бачу проблему, яку вони намагаються вирішити, але іноді простота краще зручності. Повторювати кореневої селектор не так і складно, а також робить код більш зручним для читання, т. к. в такому варіанті менше наслідувань і сам код ближче до стандартного CSS.