Цикли в CSS препроцессорах

455

Від автора: якщо ви дивилися старі фантастичні фільми, то повинні знати, наскільки цикли там потужна штука. Скорміть роботу нескінченний цикл і бабах. Залишиться від робота тільки пил. Цикли в препроцессорах не викликають драматичних вибухів в космосі (сподіваюся), але вони дуже корисні при написанні чистого CSS коду. Поки всі говорять про бібліотеки шаблонів і модульному дизайні, основна увага прикута до CSS селекторам. Неважливо, чим ви пишіть селектори (BEM, OOCSS, SMACSS, ETC), цикли додадуть читабельності вашим шаблонів і спростять роботу з ними, заклавши їх в ваш код.

Ми розглянемо можливості циклів, а також як їх використовувати в основних CSS препроцессорах: Sass, Less та Stylus. У кожної мови свій унікальний синтаксис, але всі вони роблять одне і те ж. Зациклити кішку можна різними способами.

PostCSS теж популярний, але у нього немає свого синтаксису. Іноді його називають постпроцессором, я б назвав його метапроцессором. PostCSS дозволяє писати і ділитися синтаксисом свого власного препроцесора. Якщо б ви захотіли, ви б могли переписати Sass або Less в PostCSS, але хтось уже випередив вас.

Умови циклу

Зоряний шлях був від частини прав. Якщо необачно підійти до справи, нескінченний цикл може уповільнити або зламати компілятор. Не самий кращий спосіб перетворити робота в пар, але нескінченні цикли можуть дратувати тих, хто використовує ваш код. Ось чому цикли завжди повинні розв’язувати обмежені завдання. Зазвичай задається ряд повторів з інкрементом або ставиться завдання по збору об’єктів.

З програмування:

Цикли while – загальні, вони виконуються, поки дотримується умова. Будьте обережні! Зазвичай саме тут виникають нескінченні цикли.

Цикли for – інкрементние, повторюються задане число разів.

Цикли for-each проходяться по колекції або списком, витягаючи один елемент за раз.

Кожен тип циклів вузьконаправлений, ніж попередній. Цикл for-each – приватний випадок циклу for, який в свою чергу є одним з варіантів циклу while. Однак більшість ваших випадків застосування будуть підпадати під більш конкретні категорії. Я не зміг знайти чистий цикл while на реальних сайтах, більша частина прикладів набагато краще працює на for або for-each. Можливо, тому в Stylus прописаний синтаксис тільки для останнього. У Sass є синтаксис для всіх трьох циклів, а в Less взагалі немає циклів. Але це нас не зупинить! Поїхали.

Колекція і цикли for-each

Цикли в препроцессорах найчастіше корисні, якщо вам потрібно обробити колекцію (список або масив) елементів. Наприклад, масив іконок соціальних мереж і квітів або список станів-модифікаторів (success, warning, error тощо). Цикли for-each прив’язані до відомої колекції елементів, з-за чого, як правило, це найбільш конкретні та зрозумілі цикли.

Почнемо з прогону в циклі простого списку кольорів, щоб зрозуміти принцип роботи.

У Sass для одержання квітів нам знадобиться скористатися директивою @each (@each $item in $list):

В Stylus з колекціями працює синтаксис for (for item in list):

В Less немає синтаксису для циклів, але їх можна зімітувати за допомогою рекурсії. Рекурсія – це коли ви викликаєте функцію або міксин із самої себе. В Less для рекурсії можна використовувати міксини:

.recursion() {
/* нескінченний рекурсивний цикл! */
.recursion();
}

Тепер давайте додамо умова when у міксин, щоб цикл не був нескінченним.

.recursion() when (@conditions) {
/* рекурсивний цикл “while” з умовою! */
.recursion();
}

Цей цикл можна перетворити в for, якщо додати лічильник (@i), що починається з 1 з постійним інкрементом на кожному повторенні (@i + 1), поки буде виконуватися наша умова (@i <= length(@list)). Де length(@list) обмежує кількість ітерацій довжиною нашої колекції. Якщо при кожному проході витягувати наступний елемент списку, ми отримаємо саморобний цикл for-each:

В Less все набагато складніше. Загартовує характер.

Кнопки соціальних мереж

Проганяти списки в циклі корисно, але найчастіше ви будете проходити в циклі по об’єктах. Один з найпоширеніших прикладів – привласнення різних кольорів і іконок кнопок соціальних мереж. Для кожного елемента списку, нам необхідно назву сайту і колір бренду цієї соціальної мережі:

$social: (
‘facebook’: #3b5999,
‘twitter’: #55acee,
‘linkedin’: #0077B5,
‘google’: #dd4b39,
);

У Sass щоб отримати доступ до пари ключ/значення (назва мережі/колір бренду), нам знадобиться синтаксис @each $key, $value in $array. Нижче представлений повний цикл:

В Stylus схожий синтаксис: for key, value in array

В Less необхідно витягувати частини пари вручну:

Додаткове цикли for

Цикли for можуть повторюватися набагато більше, ніж довжина об’єкта. З їх допомогою можна створювати grid макети (for columns from 1 through 12), пробегаться за колірного колеса (for from hue 1 through 360) і рахувати кількість тегів div з псевдоклассом nth-child і згенерованим контентом.

Давайте почнемо з того, що пробіжимося в циклі через 36 елементів div, будемо присвоювати їм номери і задавати фон за допомогою :nth-child.

У Sass для цього є спеціальний синтаксис for: @for $count from $start through $finish, де $start і $finish – цілочисельні значення. Якщо перше значення більше другого, Sass запустить зворотний відлік.

Ключове слово through означає, що наш цикл буде повторюватися 36 разів. Також можна використовувати ключове слово to, але тоді останнє значення не буде включатися: @for $i from 1 to 36 повториться тільки 35 разів.

В Stylus реалізований подібний синтаксис инкрементации, тільки і through to замінені на … і .. відповідно:

В Stylus також є функція range(), в якій можна змінювати значення инкремента. Код for hue in range(0, 360, 10) збільшує лічильник на 10 у кожному повторенні.

В Less нам знову доведеться використовувати рекурсивні міксини. Для кількості ітерацій (@i) можна створити аргумент, додавши умова when (@i > 0), віднімаючи одиницю на кожній ітерації. Так ми зімітуємо зменшується цикл:

Варто відзначити, що нумерацію з допомогою nth-child в CSS можна написати і без препроцесорів. В CSS немає структури циклів, але в ньому є counter() – функція, яку можна инкрементировать за будь-DOM умов. Дану функцію можна використовувати в генерованому контенті. На жаль, її не можна використовувати за межами властивості content (поки що), тобто наш фон не застосується:

Система сіток

Час від часу я використовую інкрементние цикли в абстрактному Sass, але майже ніколи в цих стилях. Виняток становлять тільки генеруються нумеровані селектори, створювані з допомогою nth-child (як у прикладі вище) або за допомогою автоматично генеруються класів (часто використовуються в системах сіток). Давайте побудуємо просту рідку систему сіток без роздільників, щоб ускладнити обчислення:

Кожен елемент сітки представлений у відсотках. Значення обчислюється за формулою span / context * 100% — стандартне обчислення для всіх систем сіток. Приклад Stylus і Less:

Унікальні аватарки

Не так давно на сайті OddBird ми проектували додаток з дефолтними аватарками користувачів. Але ми хотіли, щоб дефолтні аватарки були максимально унікальними. Зрештою, ми створили лише 9 унікальних ікон і використовували цикли, щоб трансформувати їх у 1296 різних аватарок. Більшість користувачів ніколи не побачать дублікат.

У всіх аватарів 5 атрибутів:

<svg class=”avatar” data-dark=”1″ data-light=”2″ data-reverse=”true” data-rotation=”3″>
<use xlink:href=”#icon-avatar-1″ xmlns:xlink=”http://www.w3.org/1999/xlink”></use>
</svg>

форма початковій іконки (9 варіантів);

поворот на 0, 90, 180 або 270 градусів (4 варіанти);

темний колір заливки (6 варіантів);

світлий колір фону (6 варіантів);

атрибут true/false, інвертується кольору (2 варіанти).

У коді 6 кольорів і три цикли:

@for $i from 0 through 3 дає нам 4 повороту;

@for $i from 1 through length($colors) дозволяє пробегаться в циклі за списком кольорів ($colors) і присвоювати кожному кольору число ($i). Зазвичай я б скористався циклом @each для колекції кольорів, але з циклом @for легше, коли потрібно присвоїти число кожного елемента;

Вкладений @each $in reverse (true, false) дозволяє нам перевертати передній і задній фон для кожної комбінації кольору.

Кінцевий результат Sass:

Конвертувати код в Less та Stylus можете самі, це буде ваша домашня робота. Я втомився дивитися на цей код.

Загальні цикли while

Цикли while використовуються рідко, але я час від часу використовую їх. Для мене вони найбільше корисні, коли мені потрібно проїхати по якомусь шляху і дізнатися, що наприкінці. Я не хочу бігати в циклі через всю колекцію або задане число ітерацій, я хочу бігати в циклі доти, поки не знайду те, що шукав. Такі цикли я використовую в абстрактних засобах, але це не щось, що вам буде потрібно кожен день стилях.

Я створив інструмент, який зберігає і маніпулює квітами в Sass. Зберігання квітів у змінних – напевно, самий частий випадок використання в будь-яких препроцессорах. Більшість робить так:

$pink: #E2127A;
$brand-primary: $pink;
$site-background: $brand-primary;

Розумію, що pink, швидше за все, не єдиний колір на вашому сайті, але мені поки що знадобиться тільки він. Я спеціально привласнив кілька імен, так як корисно задавати шар абстракції – від простих квітів (pink) до більш широких шаблонів (brand-primary) і конкретних кейсів (site-background). Також я хочу перевести список кольорів у палітру, яку буде розуміти мій препроцесор. Я повинен сказати, що всі ці значення пов’язані і входять в шаблон. Для цього я зберігаю всі кольори теми у одній карті Sass пар ключ-значення:

$colors: (
‘pink’: #E2127A,
‘brand-primary’: ‘pink’,
‘site-background’: ‘brand-primary’,
);

Навіщо? Так я можу вказати генератор стилів в одну змінну і автоматично створити оновлену палітру кольорів. Але є і побічні ефекти, спосіб не для всіх. Карта не дозволяє мені робити пряме призначення в парах, як я можу це робити з змінними. Щоб знайти значення для кожного кольору, мені потрібно пройти шлях із ключових імен в циклі while:

Я завжди так роблю, але якщо ви спробуєте пошукати мій код для Sass циклу @while, ви не знайдете його. Тому що те ж саме можна написати з допомогою рекурсивної функції, що зробить код повторно:

Тепер функцію color() можна викликати в будь-якому місці коду. В Stylus немає синтаксису для циклів while, але в ньому є масиви і рекурсивні функції:

В Less немає вбудованих масивів, але їх можна зімітувати, створивши список з парами, як ми робили для квітів соціальних мереж:

@colors:
‘pink’ #E2127A,
‘brand-primary’ ‘pink’,
‘site-background’ ‘brand-primary’
;

Нам доведеться створити свій власний міксин @array-get, щоб витягувати значення з масиву за ключам, після чого створити свій рекурсивний цикл while, щоб слідувати шляху:

В рамках демонстрації код працює, але в Less це можна зробити набагато краще, так як там можна задавати простір імен для змінних без масиву (на відміну від Sass і Stylus):

Тепер, коли мої кольори успішно зібрані в однієї змінної, я можу з допомогою іншого циклу згенерувати палітру кольорів. Приклад Sass:

Упевнений, ви можете написати набагато краще, ніж я.

Зациклюємося!

Якщо ви не впевнені, коли використовувати цикли в своєму коді, а коли ні, слідкуйте за повтореннями. У вас в коді є безліч селекторів, наступних схожим шаблоном, або обчислення, які постійно повторюються? Як зрозуміти, який цикл краще підходить:

Якщо ви можете перерахувати елементи в циклі і присвоїти їм імена, використовуйте цикл for-each.

Якщо важливіше кількість повторень, ніж набір вихідних елементів, або якщо потрібно дізнатися загальна кількість елементів, використовуйте цикл for.

Якщо в один цикл потрібно входити з різними даними, спробуйте використовувати рекурсивні функції.

Для всього іншого (майже ніколи) використовуйте цикл while.

Якщо працюєте на Less… удачі!