Переменные в LESS

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

Рис.9 Переменные в Less.

Переменная (variable) в LESS это поименованная область памяти, адрес (имя) которой можно использовать для осуществления доступа к заданному значению, и при необходимости изменять это значение в ходе объявления стилей. Другими словами это контейнер в который мы помещаем интересующее нас значение, и в любой момент мы можем воспользоваться этим значением, сославшись в нашем коде на этот контейнер (переменную).

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

Объявление переменной

Прежде чем использовать переменную в коде её необходимо объявить внутри файла less, или другими словами инициализировать. Имя переменной всегда должно начинаться со знака @. Если вы хорошо знакомы с CSS, то вы знаете, что в каскадных таблицах стилей знак @ указывает, что перед нами директива, или еще их называют правилами. Полный перечень директив (правил) вы можете найти в следующем разделе, не путайте их с переменными.

Синтаксис переменных всегда должен состоять из пар имя/значение:

@variableName: value; // имя и значение переменной

Давайте перейдем к рассмотрению простого примера объявления переменных в LESS и постепенно будем увеличивать сложность примеров по ходу статьи. Допустим нам необходимо разместить кнопку (элемент <button>), которая имеет одни цветовые стили для текста и границ элемента. Для этих целей создадим переменную:

@button-color: #b05049; // объявляем (инициализируем) переменную

После того как мы объявили переменную мы можем воспользоваться ей в своем коде, делается это следующим образом:

@button-color: #b05049; // объявляем (инициализируем) переменную

button {
	color: @button-color; // используем переменную в качестве значения свойства
	border: 1px solid @button-color; // используем переменную в качестве части значения свойства
}

Как вы можете догодаться после компиляции в CSS мы получим следующий результат:

button {
	color: #b05049; // цвет текста
	border: 1px solid #b05049; // стили для границ элемента
}

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

@me: "I am string";
@not-me: "me";

.selector {
	content: @@not-me; //  @@not-me --> @me --> "I am string"
}

В этом примере мы указали для CSS свойства content значение переменной, которое содержит в себе имя другой переменной. В ходе компиляции для начала будет найдено значение переменной @not-me, оно будет подставлено для поиска значения, получившейся переменной @me, которое и содержит финальный результат "I am string".

.selector {
	content: "I am string";
}

Хочу обратить Ваше внимание на то, что такая конструкция метаязыка LESS имеет право на жизнь, но встречается на практике очень редко, в основном для решения задач, которые можно решить и другим способом.

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

button {
	color: @button-color; // используем переменную в качестве значения свойства
}

@button-color: #b05049; // объявляем переменную
@button-color: red; // объявляем одноименную переменную

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

button {
	color: red; // цвет текста
}

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

Область видимости переменных

В LESS, как и, например, в языках программирования JavaScript, PHP, или C# существует понятие области видимости переменных, она представляет из себя отдельный блок кода, в котором мы инициализируем переменную. Переменные в LESS бывают как глобальными, так и локальными. В этой статье мы уже с Вами сталкивались с объявлением глобальных переменных, например:

@section: #777; // объявляем глобальную переменную
@section_inside: #999; // объявляем глобальную переменную

div {
	background: @section; // используем переменную в качестве значения свойства
	> div { // селектор дочерних элементов (выбираем элементы <div>, вложенные в <div>
		background: @section_inside; // используем переменную в качестве значения свойства
	}
}

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

div {
	background: #777777; // цвет заднего фона
}
div > div {
	background: #999999; // цвет заднего фона
}

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

Давайте рассмотрим пример, в котором мы создадим одноименные переменные, которые имеют как локальную, так и глобальную область видимости:

@myVariable: violet; // объявляем глобальную переменную
aside {
	@myVariable: yellow; // объявляем локальную переменную
	background: @myVariable // используем переменную в качестве значения свойства
}
@myVariable: red; // объявляем глобальную переменную

В связи с тем, что у нас имеется одноименная локальная переменная, то в результате компиляции будет использовано именно её значение, так как эта переменная является последней переменной, которая была объявлена в текущей области видимости (локальная область видимости).

Результат компиляции будет следующий:

aside {
	background: #ffff00; // желтый цвет в шестнадцатиричной системе
}

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

article {
	p {
		@myVariable: green; // объявляем локальную переменную
		color: @myVariable; // используем переменную в качестве значения свойства
		span {
			color: @myVariable; // используем переменную в качестве значения свойства
		}
	}
}

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

Обратите внимание на следующий пример в котором мы попытаемся воспользоваться локальной переменной, которая была инициализирована у вложенного элемента:

article {
	color: @myVariable; // переменная не доступна (undefined) - ошибка компиляции
	p {
		@myVariable: green; // объявляем локальную переменную
		color:  @myVariable; // используем переменную в качестве значения свойства
		span {
			color:  @myVariable; // используем переменную в качестве значения свойства
		}
	}
}

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

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

.selector {
	width: @my-var;
	@my-var2: 10%;
}

@my-var: @my-var2;
@my-var2: 100%;

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

.selector {
	width: 10%;
}

В этом примере мы в качесте значения свойства width присваеваем переменную @my-var, компилятор находит эту переменную в глобальной области видимости, значение этой переменной соответствует имени переменной @my-var2. Теперь внимательно присмотритесь, у нас есть переменная @my-var2 как в локальной, так и глобальной области видимости. Компилятор выберет последнюю переменную, которая была объявлена в текущей области видимости, в нашем случае это локальная область видимости.

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

@var: 0; // глобальная область видимости
.class {
	@var: 1; // локальная область видимости
	property: @var;
	.nested-class { // вложенный класс
		@var: 2;  // локальная область видимости
		property: @var;
		@var: 3;  // локальная область видимости
	}
}

Интерполяция переменных

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

В первую очередь рассмотрим пример интерполяции переменной внутри и вместо CSS свойств:

@myProperty: color; // инициализируем переменную

body {
	@{myProperty}: #222; // интерполяция переменной в качестве свойства
	background-@{myProperty}: #333; // интерполяция переменной в качестве части свойства
}

Обратите внимание на необходимость соблюдения определенного синтаксиса при проведении интерполяции, в котором необходимо помещать имя, интересующей нас переменной внутри конструкции:

@{} // имя переменной помещается между фигурных скобок

Результат компиляции нашего примера будет следующий:

body {
	color: #222; // цвет текста
	background-color: #333; // цвет заднего фона
}

В следующем примере мы произведем интерполяцию переменной внутри значения CSS свойства:

@path: "/wwwroot/images/"; // инициализируем переменную

body {
	background: url("@{path}main.png"); // интерполяцию переменной внутри значения
	header {
		background: url("@{path}header.png"); // интерполяцию переменной внутри значения
		nav {
			background: url("@{path}nav.png"); // интерполяцию переменной внутри значения
		}
	}
}

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

body {
	background: url("/wwwroot/images/main.png"); // фоновое изображение для элемента
}
body header {
	background: url("/wwwroot/images/header.png"); // фоновое изображение для элемента
}
body header nav {
	background: url("/wwwroot/images/nav.png"); // фоновое изображение для элемента
}

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

@my-selector: main-logo; // инициализируем переменную

section .@{my-selector} { // интерполяцию переменной внутри селектора
	display: none; // элемент не будет отображаться
}

По аналогии с предыдущими примерами для интерполяции переменной достаточно лишь поместить её внутри конструкции @{}. После компиляции в CSS мы получим следующий результат:

section .main-logo {
	display: none; // элемент не будет отображаться
}

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

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

~"value" // синтаксис с двойной кавычкой
~'value'  // синтаксис с одинарной кавычкой

Не будем тратить время и сразу перейдем к примеру:

@myVariable: myValue; // инициализируем переменную

body:before {
	filter: ~"ms:special.browser.syntax()"; // код будет скомпилирован только с экранированием (указано не стандартное значение)
	content: ~"@{myVariable}"; // интерполяция внутри экранирования
}

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

Главное правило экранирования, которое необходимо понять: все, что Вы поместите внутри ~"" будет скомпилировано без изменений, за исключением использования интерполяции внутри конструкции экранирования.

Результат компиляции:

body:before {
	filter: ms:special.browser.syntax(); // не стандартное значение свойства
	content: myValue; // строковое содержимое свойства
}

Свойства как переменные

С выходом LESS версии 3.0 стало доступно использование такого синтаксиса, при котором имена объявленных свойств могут использоваться в качестве пременной, для этого достаточно вызвать интересующее Вас свойство со следующим синтаксисом:

.myClass {
	color: #fff;
	background: $color; // используем имя свойства как переменную
}

Если вы используете LESS версии 3.0 или старше, то результат компиляции должен быть следующий:

.myClass {
	color: #fff;
	background: #fff;
}

Давайте рассмотрим следующий пример, в котором используем свойство в качестве переменной для вложенного селектора:

.parent {
	color: #fff;
	.nested {
		background: $color;  // используем имя свойства как переменную
	}
	color: #000;
}

Обратите внимание, что, как и при работе с обычными переменными компилятор LESS выберет последнее свойство в текущей, или родительской области видимости в качестве значения, которое будет установлено.

Если вы используете LESS версии 3.0 или старше, то результат компиляции должен быть следующий:

.parent {
	color: #fff; 
	color: #000;  
}
.parent .nested {
	background: #000; 
}