Динамический язык стилевой разметки

LESS расширяет CSS динамическими возможностями, такими как переменные, примешивания, операции и функции. LESS может использоваться как на стороне клиента (IE 6+, Webkit, Firefox), так и на стороне сервера, с Node.js и Rhino.

версия 1.3.3

Пишем на LESS:

@base: #f938ab;

.box-shadow(@style, @c) when (iscolor(@c)) {
  box-shadow:         @style @c;
  -webkit-box-shadow: @style @c;
  -moz-box-shadow:    @style @c;
}
.box-shadow(@style, @alpha: 50%) when (isnumber(@alpha)) {
  .box-shadow(@style, rgba(0, 0, 0, @alpha));
}
.box { 
  color: saturate(@base, 5%);
  border-color: lighten(@base, 30%);
  div { .box-shadow(0 0 5px, 30%) }
}

Подключаем less.js с нашими стилями:

<link rel="stylesheet/less" type="text/css" href="styles.less">
<script src="less.js" type="text/javascript"></script>

Переменные

Переменные позволяют определить постоянно используемые значения в одном месте, а затем повторно использовать их в любом месте таблицы стилей, что облегчает внесение глобальных изменений буквально до изменения одной строки кода.

  // LESS

@color: #4D926F;

#header {
  color: @color;
}
h2 {
  color: @color;
}
/* Скомпилированный CSS */

#header {
  color: #4D926F;
}
h2 {
  color: #4D926F;
}

Примешивания (mixins)

Примешивания (mixins) позволяют включать все свойства класса в другой класс путем простого включения имени класса как значение одного из свойств. Это напоминает использование переменных, но в отношении целых классов. Примешивания могут вести себя как функции, и принимать аргументы, как показано в нижеследующем примере.

// LESS

.rounded-corners (@radius: 5px) {
  border-radius: @radius;
  -webkit-border-radius: @radius;
  -moz-border-radius: @radius;
}

#header {
  .rounded-corners;
}
#footer {
  .rounded-corners(10px);
}
/* Скомпилированный CSS */

#header {
  border-radius: 5px;
  -webkit-border-radius: 5px;
  -moz-border-radius: 5px;
}
#footer {
  border-radius: 10px;
  -webkit-border-radius: 10px;
  -moz-border-radius: 10px;
}

Вложенные правила

Вместо создания длинных имен селекторов для указания наследования, в Less мы можем просто вкладывать селекторы в другие селекторы. Это делает наследование прозрачнее для понимания, а таблицы стилей — короче.

// LESS

#header {
  h1 {
    font-size: 26px;
    font-weight: bold;
  }
  p { font-size: 12px;
    a { text-decoration: none;
      &:hover { border-width: 1px }
    }
  }
}

/* Скомпилированный CSS */

#header h1 {
  font-size: 26px;
  font-weight: bold;
}
#header p {
  font-size: 12px;
}
#header p a {
  text-decoration: none;
}
#header p a:hover {
  border-width: 1px;
}

Функции и операции

Встречаются ли в ваших таблицах стилей элементы, пропорциональные другим элементам? Операции позволяют увеличивать, уменьшать, делить и умножать и значения и цвета свойств, давая возможность указывать сложные соотношения между свойствами. Функции используются точь-в-точь как в JavaScript-коде, позволяя манипулировать значениями так, как Вам угодно.

// LESS

@the-border: 1px;
@base-color: #111;
@red:        #842210;

#header {
  color: @base-color * 3;
  border-left: @the-border;
  border-right: @the-border * 2;
}
#footer { 
  color: @base-color + #003300;
  border-color: desaturate(@red, 10%);
}

/* Скомпилированный CSS */

#header {
  color: #333;
  border-left: 1px;
  border-right: 2px;
}
#footer { 
  color: #114411;
  border-color: #7d2717;
}

Использование на стороне клиента

Подключите таблицу стилей .less при помощи указания rel со значением “stylesheet/less”:

<link rel="stylesheet/less" type="text/css" href="styles.less">

Затем скачайте less.js по ссылке вверху этой страницы, и подключите его в секции <head> вашей страницы, вот так:

<script src="less.js" type="text/javascript"></script>

Обратите внимание, что важно подключить таблицу стилей до скрипта.

Режим слежения

Режим слежения — финкция на стороне клиента, при использовании которой стили обновляются автоматически по мере изменения.

Для того, чтобы включить ее, допишите ‘#!watch’ к адресу в строке браузера, а затем обновите страницу. В качестве альтернативы, Вы можете запустить less.watch() из отладочной консоли.

Использование на стороне сервера

Установка

Простейший способом установить поддержку LESS на сервер — является использование npm, менеджера пакетов node, таким образом:

$ npm install less

Использование

После установки, Вы можете вызывать компилятор Less из node, вот так:

var less = require('less');

less.render('.class { width: 1 + 1 }', function (e, css) {
    console.log(css);
});

что выведет

.class {
  width: 2;
}

Вы также можете вручную вызывать парсер и компилятор:

var parser = new(less.Parser);

parser.parse(function (err, tree) {
    if (err) { return sys.error(err) }
    console.log(tree.toCSS());
});

Конфигурация

Вы можете передать некоторые параметры конфигурации прямо в компилятор:

var parser = new(less.Parser)({
    paths: ['.', './lib'], // указывает пути поиска для директив @import
    filename: 'style.less' // указывает имя файла, для улучшения сообщений об ошибках
});

parser.parse('.class { width: 1 + 1 }', function (e, tree) {
    tree.toCSS({ compress: true }); // сжимать (minify) результирующий CSS
});

Использование из командной строки

Комплект Less содержит в себе исполняемый файл, что дает возможность вызывать компилятор из командной строки, например:

$ lessc styles.less

в результате скомпилированный CSS будет выведен в stdout, и его можно будет перенаправить в файл с именем по Вашему выбору:

$ lessc styles.less > styles.css

Для вывода сжатого (minified) таблицы CSS следует указать опцию -x. Если Вы хотите еще больше сжать выводимый код, возможен вызов YUI CSS Compressor — для этого добавьте опцию --yui-compress.

Язык

Как расширение CSS, LESS не только обладает обратной совместимостью с CSS, но использует существующий синтаксис CSS в добавляемых расширениях описаний. Это делает изучение LESS необыкновенно легким, и, в случае сомнений, позволяет вернуться к использованию чистого CSS.

Переменные

Эта возможность вполне очевидна:

@nice-blue: #5B83AD;
@light-blue: @nice-blue + #111;

#header { color: @light-blue; }

что дает на выходе:

#header { color: #6c94be; }

Можно также определять переменную, ссылаясь на другую переменную по ее имени:

@fnord: "I am fnord.";
@var: 'fnord';
content: @@var;

Что компилируется в:

content: "I am fnord.";

Заметьте, что то, что мы называем переменными — на самом деле ‘константы’, поскольку они могут быть определены только раз.

Примешивания

В LESS возможно включать целый набор свойств из одного набора правил в другой набор. Скажем, мы имеем следующий класс:

.bordered {
  border-top: dotted 1px black;
  border-bottom: solid 2px black;
}

И мы хотим использовать эти свойства внутри другого набора правил. Что же, мы просто указываем имя класса в любом другом наборе правил, куда нужно включить свойства, вот так:

#menu a {
  color: #111;
  .bordered;
}
.post a {
  color: red;
  .bordered;
}

Свойства класса .bordered теперь появятся и в #menu a, и в .post a. Результатом компиляции окажется:

#menu a {
  color: #111;
  border-top: dotted 1px black;
  border-bottom: solid 2px black;
}
.post a {
  color: red;
  border-top: dotted 1px black;
  border-bottom: solid 2px black;
}

Любой класс CSS, а также набор правил id или элемента может быть смешан точно так же.

Параметризированные примеси

В LESS существует особый вид наборов правил, которые можно смешивать, точно как описываемые выше классы, но которые принимают параметры. Вот канонический пример:

.border-radius (@radius) {
  border-radius: @radius;
  -moz-border-radius: @radius;
  -webkit-border-radius: @radius;
}

А вот как мы можем подмешивать этот набор правил в другие наборы:

#header {
  .border-radius(4px);
}
.button {
  .border-radius(6px);  
}

Параметризированные примеси могут также иметь значения по-умолчанию для своих параметров:

.border-radius (@radius: 5px) {
  border-radius: @radius;
  -moz-border-radius: @radius;
  -webkit-border-radius: @radius;
}

и, если мы используем такой вызов:

#header {
  .border-radius;  
}

то получим на выходе значение border-radius, равное 5px.

Вы также можете использовать параметризированные примеси, которые не учитывают передаваемых параметров. Это удобно, если есть желание спрятать некий набор правил при выводе в CSS, но использовать значения его свойств в других наборах правил:

.wrap () {
  text-wrap: wrap;
  white-space: pre-wrap;
  white-space: -moz-pre-wrap;
  word-wrap: break-word;
}

pre { .wrap }

Это даст на выходе:

pre {
  text-wrap: wrap;
  white-space: pre-wrap;
  white-space: -moz-pre-wrap;
  word-wrap: break-word;
}

Переменная с именем @arguments

Название переменной @arguments внутри примешивания имеет особое значение. В процессе компиляции заменяется на все переданные аргументы. Это удобно, если нет желания возиться с отдельными параметрами:

.box-shadow (@x: 0, @y: 0, @blur: 1px, @color: #000) {
  box-shadow: @arguments;
  -moz-box-shadow: @arguments;
  -webkit-box-shadow: @arguments;
}

.box-shadow(2px, 5px);

Совпадение с шаблоном и предохранительные выражения

Иногда, Вы можете захотеть изменить поведение примешивания, в зависимости от переданный в него параметров. Попробуем на чем-нибудь простом:

.mixin (@s, @color) { ... }

.class {
  .mixin(@switch, #888);
}

Теперь, предположим, что мы хотим, чтобы .mixin вела себя по-разному, в зависимости от величины @switch. Определяем .mixin таким образом:

.mixin (dark, @color) {
  color: darken(@color, 10%);
}

.mixin (light, @color) {
  color: lighten(@color, 10%);
}
.mixin (@_, @color) {
  display: block;
}

Теперь, если мы используем:

@switch: light;

.class {
  .mixin(@switch, #888);
}

Мы получим такой CSS:

.class {
  color: #a2a2a2;
  display: block;
}

Т.е. величина цвета (@color), переданная в .mixin, оказалась осветлена. Если бы значение @switch было задано как dark, результатом стал бы более темный цвет.

Вот что происходит:

  • Первое определение примешивания не подходит, поскольку мы ожидаем black в качестве первого аргумента.
  • Второе определение примешивания подходит, поскольку мы ожидаем light.
  • Третье определение примешивания срабатывает, так как оно ожидает любую величину.

Отработали только подошедшие под определение примешивания. Переменные могут совпадают и привязываться к любому значению. Все, что отличается от переменной, дает совпадение, только если равно самому себе.

Мы можем также сравнивать арность, т.е. количество элементов, например:

.mixin (@a) {
  color: @a;
}

.mixin (@a, @b) {
  color: fade(@a, @b);
}

Если мы вызовем .mixin с одним аргументом, мы получим вывод первого определения, но если мы вызовем примешивание с двумя аргументами, мы получим второе определение, а именно @a, приглушенное на @b.

Предохранители

Предохранители полезны, когда мы хотим проверять совпадение с выражениями, а не с просыми величинами или с арностью. Если Вы знакомы с функциональным программированием, Вы вероятно уже встречались с ними.

В попытке оказаться как можно ближе к декларативной природе CSS, LESS использует условное исполнение при помощи предохраняемых примешиваний вместо выражение if/else, в русле спецификации указания @media.

Начнем с примера:

.mixin (@a) when (lightness(@a) >= 50%) {
  background-color: black;
}

.mixin (@a) when (lightness(@a) < 50%) {
  background-color: white;
}
.mixin (@a) {
  color: @a;
}

Ключевым являемся указание when, которое описывает предохранительное выражение (здесь всего один предохранитель). Теперь, если запустить код:

.class1 { .mixin(#ddd) }
.class2 { .mixin(#555) }

Мы получим:

.class1 {
  background-color: black;
  color: #ddd;
}
.class2 {
  background-color: white;
  color: #555;
}

Полный список операторов сравнения в предохраняющих выражениях: > >= = =< <. Кроме того, ключевое слово true - всегда дающее истину выражение, делающее две следующие примеси эквивалентными:

.truth (@a) when (@a) { ... }

.truth (@a) when (@a = true) { ... }

Любые выражения, кроме true, являются ложью:

.class {
  .truth(40); // Не совпадет ни с одним из выражений.
}

Предохранители могут разделяться запятыми ‘,’ — если однин из них срабатывает, весь набор считается совпадшим:

.mixin (@a) when (@a > 10), (@a < -10) { ... }

Заметьте, что можно сравнивать аргументы друго с другом, либо с не-аргументами:

@media: mobile;

.mixin (@a) when (@media = mobile) { ... }

.mixin (@a) when (@media = desktop) { ... }

.max (@a, @b) when (@a > @b) { width: @a }

.max (@a, @b) when (@a < @b) { width: @b }

Наконец, если Вы хотите сравнивать примешивания по типу значения, Вы можете использовать функции вида is*:

.mixin (@a, @b: 0) when (isnumber(@b)) { ... }

.mixin (@a, @b: black) when (iscolor(@b)) { ... }

Вот функции для сравнения основных типов:

  • iscolor
  • isnumber
  • isstring
  • iskeyword
  • isurl

Если необходимо проверить, чтобы величина была не только числом, но и имела бы значение определенного типа:

  • ispixel
  • ispercentage
  • isem

Можно использовать ключевое слово and для указания дополнительных условий, например:

.mixin (@a) when (isnumber(@a)) and (@a > 0) { ... }

И ключевое слово not для отрицания условия:

.mixin (@b) when not (@b > 0) { ... }

Вложенные правила

LESS дает возможность вкладывать определения, вместо либо вместе с каскадированием. Пусть, например, у нас есть такой CSS:

#header { color: black; }
#header .navigation {
  font-size: 12px;
}
#header .logo { 
  width: 300px; 
}
#header .logo:hover {
  text-decoration: none;
}

При использовании LESS, мы также можем записать его в виде:

#header {
  color: black;

  .navigation {
    font-size: 12px;
  }
  .logo {
    width: 300px;
    &:hover { text-decoration: none }
  }
}

либо так:

#header        { color: black;
  .navigation  { font-size: 12px }
  .logo        { width: 300px;
    &:hover    { text-decoration: none }
  }
}

Итоговый код более понятен, и повторяет структуру вашего дерева DOM.

Обратите внимание на элемент & — он используется, когда указанный селектор нужно приписать к селекторe-родителю, а не использовать как вложенный элемент. Это особенно удобно для псевдо-классов типа :hover или :focus.

Например, запись:

.bordered {
  &.float {
    float: left; 
  }
  .top {
    margin: 5px; 
  }
}

даст на выходе

.bordered.float {
  float: left;  
}
.bordered .top [
  margin: 5px;
}

Операции

С любыми числовые и цветовые значениями можно производить математические операции. Вот несколько примеров:

@base: 5%;
@filler: @base * 2;
@other: @base + @filler;

color: #888 / 4;
background-color: @base-color + #111;
height: 100% / 2 + @filler;

Итоговый код будет вполне логичным — LESS распознает разницу между указаниями цветов и другими единицами измерений. Если единивы измерений используются в операции, скажем, так:

@var: 1px + 5;

LESS использует указанную единицу измерения для итогового кода — что даст, в данном случае, 6px.

Скобки также разрешены в операциях:

width: (@var + 5) * 2;

И даже обязательны в составных значениях:

border: (@width * 2) solid black;

Функции работы с цветом

LESS предлагает множество функций, изменяющих указание цвета. Цвета сначала преобразуются в цветовую модель HSL (от англ. Hue, Saturation, Lightness — цветовая модель, в которой цветовыми координатами являются тон, насыщенность и светлота), а затем манипуляции ведутся с каждым каналом по отдельности:

lighten(@color, 10%);     // возвращает цвет, который на 10% *светлее*, чем @color
darken(@color, 10%);      // возвращает цвет, который на 10% *темнее*, чем @color

saturate(@color, 10%);    // возвращает цвет, на 10% *более* насыщенный, чем @color
desaturate(@color, 10%);  // возвращает цвет, на 10% *менее* насыщенный, чем @color

fadein(@color, 10%);      // возвращает цвет, на 10% *менее* прозрачный, чем @color
fadeout(@color, 10%);     // возвращает цвет, на 10% *более* прозрачный, чем @color

spin(@color, 10);         // возвращает цвет, на 10 градусов больший по оттенку, чем @color
spin(@color, -10);        // возвращает цвет, на 10 градусов меньший по оттенку, чем @color

Использование этих функций вполне очевидно:

@base: #f04615;

.class {
  color: saturate(@base, 5%);
  background-color: lighten(spin(@base, 8), 25%);
}

Вы также можете извлекать информацию о цвете:

hue(@color);        // возвращает канал тона (`hue`) в указанном @color
saturation(@color); // возвращает канал насыщенности (`saturation`) в указанном @color
lightness(@color);  // возвращает канал светлоты (`lightness`) в указанном @color

Это полезно, если Вы хотите создать новый цвет, основываясь на значениях каналов другого цвета, например:

@new: hsl(hue(@old), 45%, 90%);

в результате @new получит тон (hue), равный тону цвета @old, но при этом будет иметь свои собственные насыщенность и светлоту.

Математические функции

LESS предлагает пару удобных математических функций, которые можно применять к числовым значениям:

round(1.67); // вернет `2`
ceil(2.4);   // вернет `3`
floor(2.6);  // вернет `2`

Если Вам нужно преобразовать значение в проценты, это можно сделать при помощи функции percentage:

percentage(0.5); // вернет `50%`

Пространства имен

Иногда появляется необходимость сгруппировать переменные или примеси, либо для удобства организации кода, либо просто для некоторой инкапсуляции. Вы можете сделать это весьма интуитивно понятно в LESS — скажем, чтобы собрать несколько примесей и переменных под #bundle, для дальнейшего использования, достаточно указать:

#bundle {
  .button () {
    display: block;
    border: 1px solid black;
    background-color: grey;
    &:hover { background-color: white }
  }
  .tab { ... }
  .citation { ... }
}

Теперь, чтобы примешать .button к нашему классу #header a, указываем:

#header a {
  color: orange;
  #bundle > .button;
}

Зоны видимости

Зоны видимости в LESS похожи на одноименное понятие, применяемые в языках программирования. Переменные и примеси сначала ищутся локально, а затем, если компилятор не смог их найти, поиск продолжается в родительской зоне видимости, затем выше, и т.д.

@var: red;

#page {
  @var: white;
  #header {
    color: @var; // white
  }
}

#footer {
  color: @var; // red  
}

Комментарии

LESS придерживается привычного по CSS стиля комментариев:

/* Привет, я комментарий в стиле CSS! */
.class { color: black }

В LESS также возможны однострочные комментарии, они считаются невидимыми, и не появляются в итоговом, скомпилированный выводе CSS:

// Привет, я невидимый комментарий, я не появлюсь в выводе CSS
.class { color: white }

Импортирование

Вы можете импортировать файлы .less, при этом все переменные и примеси в них становятся доступными в главном файле. Расширение .less необязательно, так что обе следующие записи сработают одинаково:

@import "lib.less";
@import "lib";

Если Вы хотите импортировать файл CSS, и не хотите, чтобы LESS обрабатывал его содержимое, просто используйте расширение .css:

@import "lib.css";

Директива будет оставлена как есть, и в этом же виде попадет в вывод CSS.

Интерполяция строк

Переменные могут быть включены внутрь строк, как в ruby или в PHP, при помощи конструкции @{name}:

@base-url: "http://assets.fnord.com";

background-image: url("@{base-url}/images/bg.png");

Экранирование

Иногда Вам может потребоваться вывести значение параметра CSS, либо не валидное с точки зрения CSS, либо с синтаксисом, который LESS не распознает.

В подобном случае используйте функцию e(), которой нужно передать в качестве параметра строку. Вот пример:

.class {
  filter: e("progid:DXImageTransform.Microsoft.AlphaImageLoader(src='image.png')");
}

Это будет выведено в виде:

.class {
  filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='image.png');
}

О LESS — на разных языках

О проекте

LESS был разработан Alexis Sellier, известным также, как cloudhead.