Введение
В этой статье мы поговорим о возможностях препроцессора Less, которые в силу каких-то неведомых сил, практически никто не использует. Кроме того, поговорим о недостающем функционале и попытаемся его добавить.
Начать я предлагаю с небольшого напоминания о том, как делать не нужно, даже если очень хочется. Посмотрите на фреймворк Alesya, который разработал товарищ, обучающий Less других людей. Постарайтесь внимательно посмотреть на файл ./core/functions.less
. Хотя нет, не переходите по ссылке, просто посмотрите на скриншот небольшой части этого файла. Когда первый раз увидел — я просто выпал в осадок.
Оправдание — циклы очень медленные, как следствие, скорость трансляции Less в CSS снижается, а время трансляции увеличивается. Pentium 4? Без обид, но это расстраивает.
Приведу отрывок из книги «HTML5 для веб-дизайнеров» за авторством Джереми Кит, который не так давно стал героем пабликов в ВК:
Предположим, что живет на свете злой урод, который ненавидит веб и всех пользователей Интернета. Этому господину наверняка плевать, что встраивать на страницу аудиофайл, который начинает проигрываться автоматически, невероятно грубо и глупо. С помощью атрибута autoplay можно удовлетворить его глубоко порочные желания.
<audio src="witchitalineman.mp3" autoplay></audio>
Если вы когда-нибудь будете так использовать атрибут autoplay, знайте: я вас найду.
Jeremy Keith
Поэтому, если вы будете так же использовать Less, знайте, вас уже ищут и скоро найдут.
Расширение селекторов
Многие не знают, что есть такая замечательная возможность в Less, как расширение селекторов. Проще говоря, вы из любого блока кода можете поделиться стилями одного селектора с другим. На практике это часто используется с clearfix
.
Имеем типичный для наших дней прерыватель потока:
.clearfix {
&:before,
&:after {
content: " ";
display: table;
}
&:after {
clear: both;
}
}
И какой-то селектор, который подразумевает использование clearfix. Конечно, можно использовать .clearfix
как примесь и всюду плодить огромное количество лишнего, но нужного кода. Однако, можно избежать этого, используя расширение селекторов.
Например, так:
.navbar {
&:extend(.clearfix all);
...
}
.navbar-collapse {
&:extend(.clearfix all);
...
}
.ad {
&:extend(.clearfix all);
...
}
В итоге наш класс .clearfix
начинает общаться с друзьями из различных частей ваших стилей, при этом ни с кем не делясь лишними строчками кода:
.clearfix:before,
.clearfix:after,
.navbar:before,
.navbar:after,
.navbar-collapse:before,
.navbar-collapse:after,
.ad:before,
.ad:after {
content: " ";
display: table;
}
.clearfix:after,
.navbar:after,
.navbar-collapse:after,
.ad:after {
clear: both;
}
Конечно, злоупотреблять этим не стоит. Честно говоря, советую использовать расширение селекторов только для clearfix
и в случаях, когда действует правило: «Ну очень надо, ничего поделать не могу, иначе будет хуже». Не стоит использовать расширение селекторов тогда, когда происходят махинации со стилями элементов. Наиболее частая и глупая ошибка:
.class {
color: #fff;
background: #fff;
}
.new-class {
&:extend(.class);
}
// .class,
// .new-class {
// color: #fff;
// background: #fff;
// }
Так делать нельзя. Это глупо. Лучше использовать .class
как примесь:
.class {
color: #fff;
background: #fff;
}
.new-class {
.class;
}
// .class {
// color: #fff;
// background: #fff;
// }
// .new-class {
// color: #fff;
// background: #fff;
// }
Запомните это!
Параметры импорта
Пожалуйста, обратите внимание на раздел документации «параметры директивы импорта».
Я приведу лишь краткое описание самых важных из этих параметров:
reference
Позволяет использовать .less
файлы, но не выводить их содержимого без явного вызова. Очень полезный параметр, если вы пользуетесь Bootstrap как библиотекой, а не фреймворком, то есть:
@import (reference) "bower_components/bootstrap/less/bootstrap";
.my-alert {
.alert;
}
less
Использование этого параметра даёт возможность подключать файлы не .less
расширения. Параметр не обязательный, так как и без него всё будет работать, но, тем не менее, если вы любите порядок — пригодится. Этот параметр можно применять при подключении файлов с расширением, отличным от .less
, к примеру, .variables
, .mixins
.
Применять нужно так:
@import (keyword) "filename";
С подробным описанием и примерами можно ознакомиться на страницах документации. Вот так вот вежливо я посылаю тебя, мой читатель, читать документацию. Незабываемые ощущения :)
Условные конструкции
В Less есть условные конструкции. Да, они не полноценные, если сравнивать их с другими препроцессорами, но всё таки их наличие не может не радовать. Мне они кажутся даже удобнее, чем @if в Sass, хотя практической разницы никакой нет.
.mixin(@variable) {
& when (@variable = 1) {
content: "TRUE"
}
& when not (@variable = 1) {
content: "FALSE"
}
}
.class-test {
.mixin(1);
.mixin(2);
}
В итоге мы получим следующий CSS:
.class-test {
content: "TRUE";
content: "FALSE";
}
Что же только что произошло? Магия?
На самом деле — нет. Ключевое слово when
тут заменяет привычный во всех языках программирования if
и имеет всего два логических элемента: not
— отрицание, and
— просто and и всё, больше ничего нет. Особо крутого с этим сделать не получится, но я использую условный оператор для того, чтобы не генерировался лишний код. Одной из таких ситуаций, например, является свойство border-radius
в генераторе стилей кнопки. Зачем лишний раз будет прописываться какое-либо свойство при генерации, если его значение не играет роли (0)? — Правильно, не нужно.
Интерполяция переменных
Есть в Less интересная конструкция, когда значение одной переменной мы получаем в зависимости от других.
// Variables
@clr-grey-100: #fafafa;
@clr-grey-200: #f5f5f5;
@clr-red-100: #ffebee;
@clr-red-200: #ffcdd2;
.mixin(@color, @temperature: 500) {
color: ~"@{clr-@{color}-@{temperature}}";
background: e("@{clr-@{color}-@{temperature}}");
}
.class-test {
.mixin(grey, 100);
&-test {
.mixin(red, 200);
}
}
В этом примере вызывается примесь, в которой параметрами выступают цвет (@color) и его температура (@temperature). Далее мы собираем все переменные в одну, то есть, проще говоря, конструируем новую переменную и получаем её значение. Процесс, когда строка, условно говоря, превращается в вызов переменной и есть интерполяция переменных.
Заметьте, что в этом примере мы используем фигурные скобки после собачки — это очень важно.
На выходе:
.class-test {
color: #fafafa;
background: #fafafa;
}
.class-test-test {
color: #ffcdd2;
background: #ffcdd2;
}
Кстати, такого я найти в Sass и Stylus не смог. Может подскажет внимательный читатель? :)
Циклы
Многие приверженцы препроцессоров Sass и Stylus давят на то, что в Less нет циклов, но они есть, хотя и не такие как у всех. Вот только пользоваться ими неудобно.
Допустим, что у нас есть список цветов:
@list-red: #ffebee, #ffcdd2, #ef9a9a, #e57373, #ef5350,
#f44336, #e53935, #d32f2f, #c62828, #b71c1c;
Для примера, давайте создадим для каждого цвета свой класс:
.color-generator(@index: 1, @color) when (@index <= length(@list-red)) {
// Получаем цвет по индексу
@item: extract(@list-red, @index);
// Конструируем класс на основе полученных данных
.clr-@{color}-@{index} {
color: @item;
}
// Рекурсия
.color-generator(@index + 1, @color);
}
.color-generator(@color: red);
Получим симпатичный, но не особо нужный список классов:
.clr-red-1 { color: #ffebee; }
.clr-red-2 { color: #ffcdd2; }
.clr-red-3 { color: #ef9a9a; }
.clr-red-4 { color: #e57373; }
.clr-red-5 { color: #ef5350; }
.clr-red-6 { color: #f44336; }
.clr-red-7 { color: #e53935; }
.clr-red-8 { color: #d32f2f; }
.clr-red-9 { color: #c62828; }
.clr-red-10 { color: #b71c1c; }
Пожалуй, это удобно на первых парах. Однако, это удобство исчезает, когда захочется сделать что-то внушительное, например, сетку Bootstrap. И это у них ещё все префиксы прописаны вручную и в удобном для них виде. Представляете махину, в которой префиксы колонок задаются через переменные? А если ещё и аббревиатуры колонок будут в переменных?
С циклами у Less не всё так хорошо, как у Sass или Stylus. Но честно ответьте себе на вопрос: часто ли вам нужны циклы?
More or Less
Многие разработчики, что уже знакомы с Less, используют библиотеку примесей Less Hat, которая следит за префиксами вашего CSS кода. Но я специально не упоминаю эту замечательную библиотеку, так как её необходимость уходит на второй план, если вы работаете с Autoprefixer.
Библиотека «More or Less» добавляет недостающий функционал Less. К сожалению, документация предполагает следующий принцип работы: Хочешь пользоваться моей библиотекой? — Посмотри на простейший пример, а все более-менее сложные варианты использования узнай разобравшись в коде. Это не порядок. Поэтому рассмотрим все самые интересные функции на житейских и не очень примерах.
Полноценные условные конструкции
Немногим раньше мы говорили, что условные конструкции в Less, мягко говоря, не очень. Не хватает else
. Именно такой функционал предлагает нам «More or Less». Для работы с библиотекой необходимо всего лишь её подключить и запомнить несколько простых конструкций.
Здесь и в других примерах статьи, я буду подключать все функции по отдельности, используя Bower. Поэтому, оставлю команду для установки пакета:
bower i --save more-or-less
Итак, у нас есть вот такой код:
@import "bower_components/more-or-less/less/fn/_if.less";
.class-test-if {
.if(isnumber(2), {
.-then() {
background-color: #fff;
}
.-else() {
background-color: #000;
}
});
}
На выходе:
.class-test-if {
background-color: #fff;
}
Поменяем 1 на string:
.class-test-if {
background-color: #000;
}
Замечательно. Но как сравнить, например, переменную с каким-то конкретным значением, а не типом? — Почти так же легко.
.class-test-if {
@variable: col;
.if(@variable eq col, {
.-then() {
content: "true";
}
.-else() {
content: "false";
}
});
}
Всё работает как часики:
.class-test-if {
content: "true";
}
Кстати, вы заметили ключевое слово, которое придётся запомнить? — Не беда! Ничего сложного тут нет:
- ls (less,
<
) — меньше; - lte (less-than or equal to,
<=
) — меньше или равно; - gt (greater,
>
) — больше; - gte (greater-than or equal to,
>=
) — больше или равно; - eq (equality,
=
) — равно;
Полноценный цикл for
Наиболее удобным в этой библиотеке получился стандартный во всех ЯП цикл for. Давайте подключим функцию библиотеки и рассмотрим подробнее пример.
@import "bower_components/more-or-less/less/fn/_for.less";
.class-test-for {
// Просто переменная, можно и без неё
@count: 4;
// Цикл
.for(@count);
.-each(@index) {
&-@{index} {
width: @index * (100% / @count);
}
}
}
Заметили, что синтаксис куда приятнее, чем у оригинального цикла, который мы рассматривали в начале статьи?
В этом примере генерируется простейшая сетка, без всяких фишек. Код после трансляции в CSS:
.class-test-for-1 { width: 25%; }
.class-test-for-2 { width: 50%; }
.class-test-for-3 { width: 75%; }
.class-test-for-4 { width: 100%; }
Хочу обратить ваше внимание, что автор библиотеки предлагает вот такой стиль записи циклов:
.for(@n);.-each(@i) {
...
}
Судя по всему, этот вариант записи пытается имитировать стандартную форму записи for
в других ЯП. Должен признать, что выглядит это ужасно, поэтому советую использовать вариант из этой статьи.
Конкатенация элементов массива
Для объединения всех элементов массива (списка) в одну строку используется функция join
, принимающая аргументами массив, который необходимо преобразовать в строку, и разделитель.
К слову, этот разделитель называется glue — строковый аргумент, с помощью которого будут соединены в строку все элементы массива.
.class-test-join {
// Список (массив)
@animals: 'cat', 'tiger', 'lion';
// Конкатенация элементов списка (массива)
.join(@animals, " ");
content: '@{string}';
}
На выходе получим вот такую вот строку:
.class-test-join .animals:after {
content: 'cat tiger lion';
}
Представить себе не могу, зачем это нужно в Less? Лучше бы сделал кто-нибудь функцию, которая позволяла бы добавлять записи в цикле в массив без лишней возни.
Цикл repeat
Ещё одно сомнительное изобретение, которое не может найти в моей голове практического применения.
.class-test-repeat {
.repeat('.test', ' + ', 5);
@{string} {
float: right;
}
}
Какой-то глуповатый пример из документации:
.class-test-repeat .test + .test + .test + .test + .test {
float: right;
}
Выводы
К сожалению, используя «More or Less» сетку Bootstrap построить опять таки сложно. В любом случае не обойтись без when (@index <= ...)
, чтобы создать рекурсию и собрать классы в список для дальнейшей работы. Очень уж не хватает возможности создавать массив.
После «More or Less» стало куда приятнее работать с циклом for и условием if. Остальные функции созданы для того, чтобы просто быть и показать возможности — применение им найти сложно, но можно.
Если есть острая необходимость в полноценной поддержке циклов, условий и, например, удобств в виде подключения папок или нормальных массивов, то следует присматриваться к Sass или Stylus.
Ссылки
А как же без дополнительного материала? Думали, что я вот так просто вас отпущу?
- Курс по основам Less от HTML Academy.
- Старые — новые фишки Less на Habrahabr.
- Статья о том, что можно вытворять с примесями на SitePoint.
Ничего особенного в этих статьях нет, нужно всего лишь почитать документацию.