Website development
CSS
18 August 2014

Малоиспользуемые, но от этого не менее прекрасные возможности LESS

From Sandbox
Данный пост навеян коментарием уважаемого хабраюзера SerafimArts о том, что LESS много чего не умеет. Хочется развеять эти крамольные заявления и заодно показать, каким прекрасным может быть LESS, если правильно его готовить.

Примечание: некоторые примеры «из жизни» в данной статье предоставлены для тех людей, кто по каким-то причинам (вплоть до религиозных) не использует Autoprefixer.

Примечание 2: для всего, что написано ниже используется последняя версия LESS, потому что нет вообще ни одной причины её не использовать.



Слияния


Они же объединения, они же мерджи (Merge). Используются, если вам нужно что-нибудь присоединить через пробел или через запятую. Транзишны, трасформы, множественные бэкграунды, тени (простите за русское слово: бокс-шадоуы звучит как-то неласково) ликуют. Лучше всего за меня скажут примеры.


Объединяем через пробел прямо в миксине
.transform (@value) {
    transform+_:@value;
}

.transformed {
    .transform(rotateX(10deg));
    .transform(scale(1.2,1.1))
}

Скомпилируется в
.transformed {
     transform: rotateX(10deg) scale(1.2, 1.1);
}


А теперь через запятую
.transition (@property: all,@duration: 0s,@delay: 0s,@ease: ease-in-out){
    transition+: @arguments;
}

.transitioned {
    .transition(width,1s);
    .transition(opacity,0.5s,0.2s,linear);
}

Превратится в
.transitioned {
     transition: width 1s 0s ease-in-out, opacity 0.5s 0.2s linear;
}


То что нужно. Никаких сложностей с использованием: просто вызовите нужный миксин столько раз, сколько вам надо. Это чем-то даже удобнее способа с неопределенным количеством параметров: жёсткий порядок аргументов не даст вам возможности потеряться. Счастье, да и только.

Условия


Вместо привычных if-ов в LESS существуют так называемые гарды (охранники, guards). Суть их в том, чтобы вызывать разные объявления одного миксина в зависимости от различных условий (есть в них что-то от дженериков). Пример из жизни приведен во всё том же комментарии, который меня вдохновил: транзишны для трансформов хотелось бы иметь с префиксами. Да будет так (обратите внимание на ключевое слово when после объявления миксина):

//Специальный вариант для трансформа
.transition (@property: all,@duration: 0s,@delay: 0s,@ease: ease-in-out) when (@property=transform) {
    -webkit-transition+: -webkit-transform @duration @delay @ease;
       -moz-transition+:    -moz-transform @duration @delay @ease;
         -o-transition+:      -o-transform @duration @delay @ease;
            transition+:     -ms-transform @duration @delay @ease, 
                                 transform @duration @delay @ease;  //Кажется, -ms-transition официально не существует
}

//И всё остальное.
.transition (@property: all,@duration: 0s,@delay: 0s,@ease: ease-in-out) when not (@property=transform) {
    -webkit-transition+: @arguments;
       -moz-transition+: @arguments;
         -o-transition+: @arguments;
            transition+: @arguments;
}

.transitioned {
    .transition(width,1s);
    .transition(transform,0.5s,0.2s,linear);
}


На выходе получаем (форматирование по столбцам моё, остальное сделал препроцессор)
.transitioned {
    -webkit-transition: width 1s 0s ease-in-out, -webkit-transform 0.5s 0.2s linear;
       -moz-transition: width 1s 0s ease-in-out,    -moz-transform 0.5s 0.2s linear;
         -o-transition: width 1s 0s ease-in-out,      -o-transform 0.5s 0.2s linear;
            transition: width 1s 0s ease-in-out,     -ms-transform 0.5s 0.2s linear, 
                                                         transform 0.5s 0.2s linear;
}


Ну разве это не прекрасно? Гарды поддерживают отношения =, >, <, >=, <=, логические операторы and, or, not, и ещё немного функций для проверки на тип переменной (iscolor, isnumber и т. п.), что позволяет варьировать выбор миксина в значительных пределах. Маленький, но важный нюанс: LESS возьмёт и выполнит все миксины с одним именем, которые пройдут через охрану. Это надо не забывать и дописывать все нужные условия для «остальных» случаев. И я думаю, что это хорошо. А почему это хорошо, будет видно ниже.

Циклы


Заявление, что в LESS нету циклов абсолютно верно. Но зато есть рекурсия! Собственно, её и предлагают использовать разработчики. В качестве жизненного примера создадим генератор спрайтовых классов для картинки (не пропускайте код, там много комментариев)

// Это базовый миксин для бэкграунда. Он простой, как пять копеек, но это всего лишь пример
.spriter (@x: 0,@y: 0) {
    background: #cccccc url("my-sprite-img.png") @x*20px @y*30px;
}

//Здесь непосредственно генерируется класс
.generate_sprite(@x,@y) {
    .sprite-@{x}-@{y}{
        .spriter(@x,@y);
    }
}

//Следующие две функции управляющие - задают порядок действий при обходе двумерного массива рекурсией. 
//Удобно то, что их можно называть так же, как и основную функцию-генератор. 
//LESS будет вызывать все миксины, которые удовлетворят гардам.
.generate_sprite(@x,@y) when (@y=0) and (@x>0) {
    .generate_sprite(@x - 1,@spritey);
}

.generate_sprite(@x,@y) when (@y>0){
    .generate_sprite(@x,@y - 1);
}

//Числа, которые на один меньше количества столбцов и строк иконок в спрайте
@spritex: 1;
@spritey: 2;

//Запускаем генератор
.generate_sprite(@spritex,@spritey);



На выходе получим шесть классов для иконок
.sprite-1-2 {
  background: #cccccc url("my-sprite-img.png") 20px 60px;
}
.sprite-1-1 {
  background: #cccccc url("my-sprite-img.png") 20px 30px;
}
.sprite-1-0 {
  background: #cccccc url("my-sprite-img.png") 20px 0px;
}
.sprite-0-2 {
  background: #cccccc url("my-sprite-img.png") 0px 60px;
}
.sprite-0-1 {
  background: #cccccc url("my-sprite-img.png") 0px 30px;
}
.sprite-0-0 {
  background: #cccccc url("my-sprite-img.png") 0px 0px;
}


Именно здесь мы использовали то, что вызовутся все подходящие миксины. Ведь благодаря этому нам не пришлось дублировать вызов генератора класса в управляющих структурах. Отлично же.
Наверное, вы скажете, что это извращение. Думаю, вы будете правы. Всё же отсутствие циклов в языке несколько увеличивает сложность в построении таких конструкций. Благо, нужны они не так часто.

Внесение кода как параметр


В качестве параметров можно передавать полноценные куски less-кода, которые будут себя вести вполне логично. Примеры — наше всё: показывать проще чем объяснять
.placeholder(@content) {
    &::-webkit-input-placeholder{
        @content();
    }
    &:-moz-placeholder {
        @content();
    }
    //Пожалуй, для демонстрации хватит
}

.withplaceholder{
    .placeholder({
         //Вау, мой редактор кода даже подсветит это правильно
         color:blue;
         font-style: italic;
   })
}


Лёгким движением руки превращается в
.withplaceholder::-webkit-input-placeholder {
  color: blue;
  font-style: italic;
}
.withplaceholder:-moz-placeholder {
  color: blue;
  font-style: italic;
}


Более того, вы можете во встраиваемый код вставлять вызов других миксинов и всё сработает:
.topleft(@position){
    top:@position;
    left:@position/2;
}

.keyframes(@name,@rules) {
    //Можно я обойдусь без вендорных префиксов?
    @keyframes @name {
        @rules();
    };
}

.keyframes(down,{
    0% {
        .topleft(0);
    };
    100% {
        .topleft(80);
    };
});


И на выходе
@keyframes down {
  0% {
    top: 0;
    left: 0;
  }
  100% {
    top: 80;
    left: 40;
  }
}

Создали весь пак кейфреймов (точнее создали бы, если бы я для краткости и читабельности не пропустил вендорные префиксы) одной единственной командой &mdash; идеально.

И ещё одна вкусность напоследок в этом разделе: не любите писать длинные media query? Я вас понимаю: каждый раз одно и тоже. Давайте сократим. Вот такой код
@width_mobile: 320px;
@width_tablet: 640px;

.media_mobile(@rules) {
    @media screen and (max-width: @width_tablet) and (min-width: @width_mobile) {
        @rules();
    };
};

.class_parent{
    width:300px;
    .media_mobile({
        width:200px;
        .class_child{
            width:100px;
        }
    });
}


Станет таким
.class_parent {
    width: 300px;
}
@media screen and (max-width: 640px) and (min-width: 320px) {
    .class_parent {
        width: 200px;
    }
    .class_parent .class_child {
        width: 100px;
    }
}

Не писать каждый раз длинный паровоз media query &mdash; это ли не мечта адаптивного верстальщика?

Что можно сказать в итоге. LESS очень активно развивается. Некоторых из этих фичей не было буквально до версии 1.7, которая вышла буквально полгода назад. Лично мне это доставляло некоторые неудобства: часто приходится работать с различными видами анимаций на фронтенде. Однако теперь я практически готов расцеловать разработчиков за этот милый и добрый препроцессор, который прост в освоении (не было никаких проблем в том, чтобы перевести на него даже самых неопытных разработчиков), достаточно мощен для всех (или почти всех) жизненных ситуаций и, что немаловажно, имеет удобный читабельный синтаксис.

Надеюсь, данный пост хоть немного подымет LESS в глазах хабрасообщества. Всем спасибо за внимание.

P.S. Если мне кто-нибудь может подсказать, как сделать подсветку LESS на хабре, буду очень благодарен.

+68
33.3k 387
Comments 22
Top of the day