Обзор

Открыть оригинал markdown source для "overview"

Подробный разбор возможностей языка Less. Если нужен быстрый вводный обзор, начните со страницы Обзор.

Установка, настройка окружения и материалы для разработчиков Less.js собраны в разделе Использование Less.js.


Переменные

Открыть оригинал markdown source для "variables"

Храните часто используемые значения в одном месте.

Обзор

В больших стилях одно и то же значение легко может повторяться десятки, если не сотни раз:

a,
.link {
  color: #428bca;
}
.widget {
  color: #fff;
  background: #428bca;
}

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

// Variables
@link-color:        #428bca; // sea blue
@link-color-hover:  darken(@link-color, 10%);

// Usage
a,
.link {
  color: @link-color;
}
a:hover {
  color: @link-color-hover;
}
.widget {
  color: #fff;
  background: @link-color;
}

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

Выше переменные использовались как значения CSS-свойств, но на этом их роль не заканчивается. Их можно подставлять и в другие места: в имена селекторов, имена свойств, URL и директивы @import.

Селекторы

v1.4.0

// Variables
@my-selector: banner;

// Usage
.@{my-selector} {
  font-weight: bold;
  line-height: 40px;
  margin: 0 auto;
}

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

.banner {
  font-weight: bold;
  line-height: 40px;
  margin: 0 auto;
}

URL

// Variables
@images: "../img";

// Usage
body {
  color: #444;
  background: url("@{images}/white-sand.png");
}

Директивы @import

v1.4.0

Синтаксис: @import "@{themes}/tidal-wave.less";

Обратите внимание: до версии 2.0.0 учитывались только переменные, объявленные в корневой или текущей области видимости. При поиске переменной Less смотрел только в текущий файл и цепочку вызывающих файлов.

Пример:

// Variables
@themes: "../../src/themes";

// Usage
@import "@{themes}/tidal-wave.less";

Свойства

v1.6.0

@property: color;

.widget {
  @{property}: #0ee;
  background-@{property}: #999;
}

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

.widget {
  color: #0ee;
  background-color: #999;
}

Переменные в имени переменной

В Less имя переменной тоже можно собрать через другую переменную.

@primary:  green;
@secondary: blue;

.section {
  @color: primary;

  .element {
    color: @@color;
  }
}

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

.section .element {
  color: green;
}

Ленивое вычисление

Переменную не обязательно объявлять раньше, чем она будет использована.

Корректный Less-код:

.lazy-eval {
  width: @var;
}

@var: @a;
@a: 9%;

И такой вариант тоже полностью корректен:

.lazy-eval {
  width: @var;
  @a: 9%;
}

@var: @a;
@a: 100%;

Оба фрагмента компилируются в:

.lazy-eval {
  width: 9%;
}

Если переменная определена несколько раз, Less берет последнее определение, поднимаясь от текущей области видимости вверх. Это очень похоже на сам CSS, где итоговое значение свойства определяется последним подходящим объявлением.

Например:

@var: 0;
.class {
  @var: 1;
  .brass {
    @var: 2;
    three: @var;
    @var: 3;
  }
  one: @var;
}

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

.class {
  one: 1;
}
.class .brass {
  three: 3;
}

По сути, у каждой области видимости есть свое "финальное" значение - почти как у обычных CSS-свойств. Например:

.header {
  --color: white;
  color: var(--color);  // the color is black
  --color: black;
}

Именно поэтому переменные Less по поведению гораздо ближе к CSS, чем во многих других CSS-препроцессорах.

Свойства как переменные (NEW!)

v3.0.0

Через синтаксис $prop свойства можно использовать почти как переменные. Иногда это позволяет сделать код чуть короче.

.widget {
  color: #efefef;
  background-color: $color;
}

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

.widget {
  color: #efefef;
  background-color: #efefef;
}

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

.block {
  color: red; 
  .inner {
    background-color: $color; 
  }
  color: blue;  
} 

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

.block {
  color: red;
  color: blue;
}
.block .inner {
  background-color: blue;
}

Родительские селекторы

Открыть оригинал markdown source для "parent-selectors"

Обращение к родительскому селектору через &

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

a {
  color: blue;
  &:hover {
    color: green;
  }
}

Результат:

a {
  color: blue;
}

a:hover {
  color: green;
}

Обратите внимание: без & в этом примере получился бы селектор a :hover, то есть потомок внутри <a>, находящийся в состоянии hover. Обычно это совсем не то, что требуется.

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

.button {
  &-ok {
    background-image: url("ok.png");
  }
  &-cancel {
    background-image: url("cancel.png");
  }

  &-custom {
    background-image: url("custom.png");
  }
}

Результат:

.button-ok {
  background-image: url("ok.png");
}
.button-cancel {
  background-image: url("cancel.png");
}
.button-custom {
  background-image: url("custom.png");
}

Несколько & в одном селекторе

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

.link {
  & + & {
    color: red;
  }

  & & {
    color: green;
  }

  && {
    color: blue;
  }

  &, &ish {
    color: cyan;
  }
}

Вывод:

.link + .link {
  color: red;
}
.link .link {
  color: green;
}
.link.link {
  color: blue;
}
.link, .linkish {
  color: cyan;
}

Помните, что & обозначает весь родительский селектор целиком, а не только ближайший фрагмент. Поэтому следующий пример:

.grand {
  .parent {
    & > & {
      color: red;
    }

    & & {
      color: green;
    }

    && {
      color: blue;
    }

    &, &ish {
      color: cyan;
    }
  }
}

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

.grand .parent > .grand .parent {
  color: red;
}
.grand .parent .grand .parent {
  color: green;
}
.grand .parent.grand .parent {
  color: blue;
}
.grand .parent,
.grand .parentish {
  color: cyan;
}

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

Иногда полезно поставить дополнительный селектор перед унаследованным родительским. Для этого & помещают после текущего селектора.

Например, при работе с Modernizr часто нужно задать отдельные правила в зависимости от поддерживаемых возможностей:

.header {
  .menu {
    border-radius: 5px;
    .no-borderradius & {
      background-image: url('images/button-background.png');
    }
  }
}

Селектор .no-borderradius & добавляет .no-borderradius перед родительским .header .menu, и на выходе получается .no-borderradius .header .menu:

.header .menu {
  border-radius: 5px;
}
.no-borderradius .header .menu {
  background-image: url('images/button-background.png');
}

Комбинаторный взрыв

Через & можно сгенерировать все возможные комбинации селекторов из списка, разделенного запятыми:

p, a, ul, li {
  border-top: 2px dotted #366;
  & + & {
    border-top: 0;
  }
}

Это разворачивается во все возможные 16 комбинаций указанных элементов:

p,
a,
ul,
li {
  border-top: 2px dotted #366;
}
p + p,
p + a,
p + ul,
p + li,
a + p,
a + a,
a + ul,
a + li,
ul + p,
ul + a,
ul + ul,
ul + li,
li + p,
li + a,
li + ul,
li + li {
  border-top: 0;
}

Директивы @import

Открыть оригинал markdown source для "imports"

Импортируйте стили из других файлов.

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

Пример:

.foo {
  background: #900;
}
@import "this-is-valid.less";

Расширения файлов

Less по-разному обрабатывает @import в зависимости от расширения файла:

  • Если файл имеет расширение .css, он трактуется как обычный CSS, а директива @import остается в итоговом коде как есть. См. также опцию inline ниже.
  • Если у файла любое другое расширение, Less считает его Less-файлом и импортирует как Less.
  • Если расширения нет, Less автоматически добавит .less и подключит файл как Less.

Примеры:

@import "foo";      // foo.less is imported
@import "foo.less"; // foo.less is imported
@import "foo.php";  // foo.php imported as a Less file
@import "foo.css";  // statement left in place, as-is

При необходимости это поведение можно переопределить опциями импорта.

Опции импорта

Less расширяет стандартную CSS-директиву @import, чтобы гибче управлять тем, как подключаются внешние файлы.

Синтаксис: @import (keyword) "filename";

Поддерживаются такие опции:

  • reference - использовать Less-файл, но не выводить его в результат напрямую;
  • inline - вставить исходный файл в выходной код, но не обрабатывать его;
  • less - считать файл Less-файлом независимо от расширения;
  • css - считать файл CSS-файлом независимо от расширения;
  • once - подключать файл только один раз; это поведение по умолчанию;
  • multiple - разрешить подключение одного и того же файла несколько раз;
  • optional - не останавливать компиляцию, если файл не найден.

В одном @import можно использовать несколько ключевых слов. Для этого перечисляйте их через запятую.

Пример: @import (optional, reference) "foo.less";

reference

Используйте @import (reference), если нужно подключить внешний файл, но не добавлять его стили в итоговый CSS, пока они явно не понадобятся.

Выпущено в v1.5.0

Пример: @import (reference) "foo.less";

Проще всего представить, что reference помечает каждый селектор и каждое at-правило в импортированном файле флагом "справочный". Файл импортируется как обычно, но на этапе генерации CSS такие селекторы не попадают в результат. То же касается media queries, если внутри них есть только reference-селекторы. Стили из reference-импорта попадут в итоговый CSS только в том случае, если вы используете их как миксины или через extend.

При этом у reference есть важное различие в зависимости от способа использования:

  • extend: когда селектор расширяется, только новый селектор считается "не reference", и он подтягивается в точке, где стоит @import (reference).
  • mixins: когда стиль из reference-файла используется как неявный миксин, его правила подмешиваются в результат как обычные, уже без reference-флага.

Пример reference

Так можно вытащить из библиотеки вроде Bootstrap только нужные вам стили:

.navbar:extend(.navbar all) {}

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

inline

@import (inline) позволяет вставить внешний файл в итоговый код без обработки.

Выпущено в v1.5.0

Пример: @import (inline) "not-less-compatible.css";

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

Поэтому inline удобно использовать, когда нужно просто включить файл в итоговый результат, сохранив весь CSS в одном файле.

less

@import (less) заставляет Less обрабатывать импортируемый файл как Less независимо от расширения.

Выпущено в v1.4.0

Пример:

@import (less) "foo.css";

css

@import (css) заставляет Less относиться к файлу как к обычному CSS независимо от расширения. Это значит, что директива импорта останется в результате без изменений.

Выпущено в v1.4.0

Пример:

@import (css) "foo.less";

Вывод:

@import "foo.less";

once

Поведение @import по умолчанию: файл подключается только один раз, а все последующие импорты того же файла игнорируются.

Выпущено в v1.4.0

Пример:

@import (once) "foo.less";
@import (once) "foo.less"; // this statement will be ignored

multiple

@import (multiple) разрешает подключать один и тот же файл несколько раз. Это противоположность поведения once.

Выпущено в v1.4.0

Пример:

// file: foo.less
.a {
  color: green;
}
// file: main.less
@import (multiple) "foo.less";
@import (multiple) "foo.less";

Результат:

.a {
  color: green;
}
.a {
  color: green;
}

optional

@import (optional) позволяет подключать файл только если он существует. Без ключевого слова optional Less выбросит FileError и остановит компиляцию, если файл не найден.

Выпущено в v2.3.0


Extend

Открыть оригинал markdown source для "extend"

Extend — это псевдокласс Less, который объединяет селектор, на который он установлен, с селекторами, соответствующими тому, на что он ссылается.

Выпущено v1.4.0

nav ul {
  &:extend(.inline);
  background: blue;
}

В приведенном выше наборе правил селектор :extend будет применять «расширяющий селектор» (nav ul) к классу .inline везде, где появляется класс .inline. Блок объявлений будет сохранен как есть, но без каких-либо ссылок на расширение (поскольку расширение не является CSS).

Итак следующее:

nav ul {
  &:extend(.inline);
  background: blue;
}
.inline {
  color: red;
}

Выходы

nav ul {
  background: blue;
}
.inline,
nav ul {
  color: red;
}

Обратите внимание, что селектор nav ul:extend(.inline) выводится как nav ul — расширение удаляется перед выводом, а блок селектора остается как есть. Если в этот блок не помещены никакие свойства, он удаляется из вывода (но расширение все равно может повлиять на другие селекторы).

Расширить синтаксис

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

Пример:

.a:extend(.b) {}

// the above block does the same thing as the below block
.a {
  &:extend(.b);
}
.c:extend(.d all) {
  // extends all instances of ".d" e.g. ".x.d" or ".d.x"
}
.c:extend(.d) {
  // extends only instances where the selector will be output as just ".d"
}

Он может содержать один или несколько расширяемых классов, разделенных запятыми.

Пример:

.e:extend(.f) {}
.e:extend(.g) {}

// the above and the below do the same thing
.e:extend(.f, .g) {}

Расширить, прикрепленный к селектору

Расширение, прикрепленное к селектору, выглядит как обычный псевдокласс с селектором в качестве параметра. Селектор может содержать несколько предложений расширения, но все расширения должны находиться в конце селектора.

  • Добавить после селектора: pre:hover:extend(div pre).
  • Между селектором и расширением допускается пробел: pre:hover :extend(div pre).
  • Допускаются несколько расширений: pre:hover:extend(div pre):extend(.bucket tr) - Обратите внимание, что это то же самое, что pre:hover:extend(div pre, .bucket tr)
  • Это НЕ разрешено: pre:hover:extend(div pre).nth-child(odd). Расширение должно быть последним.

Если набор правил содержит несколько селекторов, любой из них может иметь ключевое слово расширения. Несколько селекторов с расширением в одном наборе правил:

.big-division,
.big-bag:extend(.bag),
.big-bucket:extend(.bucket) {
  // body
}

Расширить внутренний набор правил

Расширение можно поместить в тело набора правил, используя синтаксис &:extend(selector). Помещение расширения в тело — это ярлык для помещения его в каждый селектор этого набора правил.

Расширить внутри тела:

pre:hover,
.some-class {
  &:extend(div pre);
}

это то же самое, что добавление расширения после каждого селектора:

pre:hover:extend(div pre),
.some-class:extend(div pre) {}

Расширение вложенных селекторов

Extend может сопоставлять вложенные селекторы. Меньше:

Пример:

.bucket {
  tr { // nested ruleset with target selector
    color: blue;
  }
}
.some-class:extend(.bucket tr) {} // nested ruleset is recognized

Выходы

.bucket tr,
.some-class {
  color: blue;
}

По сути, расширение смотрит на скомпилированный CSS, а не на исходный.

Пример:

.bucket {
  tr & { // nested ruleset with target selector
    color: blue;
  }
}
.some-class:extend(tr .bucket) {} // nested ruleset is recognized

Выходы

tr .bucket,
.some-class {
  color: blue;
}

Точное соответствие с расширением

Расширение по умолчанию ищет точное совпадение между селекторами. Имеет значение, использует ли селектор ведущую звезду или нет. Не имеет значения, что два n-го выражения имеют одинаковое значение, они должны иметь одинаковую форму, чтобы их можно было сопоставить. Единственным исключением являются кавычки в селекторе атрибутов, less знает, что они имеют одинаковое значение, и сопоставляет их.

Пример:

.a.class,
.class.a,
.class > .a {
  color: blue;
}
.test:extend(.class) {} // this will NOT match the any selectors above

Ведущая звезда имеет значение. Селекторы *.class и .class эквивалентны, но расширение не будет соответствовать им:

*.class {
  color: blue;
}
.noStar:extend(.class) {} // this will NOT match the *.class selector

Выходы

*.class {
  color: blue;
}

Порядок псевдоклассов имеет значение. Селекторы link:hover:visited и link:visited:hover соответствуют одному и тому же набору элементов, но расширение рассматривает их как разные:

link:hover:visited {
  color: blue;
}
.selector:extend(link:visited:hover) {}

Выходы

link:hover:visited {
  color: blue;
}

nth Выражение

N-я форма выражения имеет значение. N-ые выражения 1n+3 и n+3 эквивалентны, но расширение не будет им соответствовать:

:nth-child(1n+3) {
  color: blue;
}
.child:extend(:nth-child(n+3)) {}

Выходы

:nth-child(1n+3) {
  color: blue;
}

Тип цитаты в селекторе атрибутов не имеет значения. Все нижеперечисленное эквивалентно.

[title=identifier] {
  color: blue;
}
[title='identifier'] {
  color: blue;
}
[title="identifier"] {
  color: blue;
}

.noQuote:extend([title=identifier]) {}
.singleQuote:extend([title='identifier']) {}
.doubleQuote:extend([title="identifier"]) {}

Выходы

[title=identifier],
.noQuote,
.singleQuote,
.doubleQuote {
  color: blue;
}

[title='identifier'],
.noQuote,
.singleQuote,
.doubleQuote {
  color: blue;
}

[title="identifier"],
.noQuote,
.singleQuote,
.doubleQuote {
  color: blue;
}

Расширить "все"

Когда вы указываете ключевое слово all последним в аргументе расширения, оно указывает Less сопоставить этот селектор как часть другого селектора. Селектор будет скопирован, и только совпадающая часть селектора будет заменена расширением, создавая новый селектор.

Пример:

.a.b.test,
.test.c {
  color: orange;
}
.test {
  &:hover {
    color: green;
  }
}

.replacement:extend(.test all) {}

Выходы

.a.b.test,
.test.c,
.a.b.replacement,
.replacement.c {
  color: orange;
}
.test:hover,
.replacement:hover {
  color: green;
}

Вы можете думать об этом режиме работы как о неразрушающем поиске и замене.

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

Extend не может сопоставлять селекторы с переменными. Если селектор содержит переменную, расширение проигнорирует ее.

Однако расширение можно присоединить к интерполированному селектору.

Селектор с переменной не будет сопоставлен:

@variable: .bucket;
@{variable} { // interpolated selector
  color: blue;
}
.some-class:extend(.bucket) {} // does nothing, no match is found

и расширение с помощью переменной в целевом селекторе ничего не соответствует:

.bucket {
  color: blue;
}
.some-class:extend(@{variable}) {} // interpolated selector matches nothing
@variable: .bucket;

Оба приведенных выше примера компилируются в:

.bucket {
  color: blue;
}

Однако :extend, прикрепленный к интерполированному селектору, работает:

.bucket {
  color: blue;
}
@{variable}:extend(.bucket) {}
@variable: .selector;

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

.bucket, .selector {
  color: blue;
}

Обзор/расширение внутри @media

В настоящее время :extend внутри объявления @media будет соответствовать только селекторам внутри одного и того же объявления носителя:

@media print {
  .screenClass:extend(.selector) {} // extend inside media
  .selector { // this will be matched - it is in the same media
    color: black;
  }
}
.selector { // ruleset on top of style sheet - extend ignores it
  color: red;
}
@media screen {
  .selector {  // ruleset inside another media - extend ignores it
    color: blue;
  }
}

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

@media print {
  .selector,
  .screenClass { /*  ruleset inside the same media was extended */
    color: black;
  }
}
.selector { /* ruleset on top of style sheet was ignored */
  color: red;
}
@media screen {
  .selector { /* ruleset inside another media was ignored */
    color: blue;
  }
}

Примечание. Расширение не соответствует селекторам внутри вложенного объявления @media:

@media screen {
  .screenClass:extend(.selector) {} // extend inside media
  @media (min-width: 1023px) {
    .selector {  // ruleset inside nested media - extend ignores it
      color: blue;
    }
  }
}

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

@media screen and (min-width: 1023px) {
  .selector { /* ruleset inside another nested media was ignored */
    color: blue;
  }
}

Расширение верхнего уровня соответствует всему, включая селекторы внутри вложенных медиафайлов:

@media screen {
  .selector {  /* ruleset inside nested media - top level extend works */
    color: blue;
  }
  @media (min-width: 1023px) {
    .selector {  /* ruleset inside nested media - top level extend works */
      color: blue;
    }
  }
}

.topLevel:extend(.selector) {} /* top level extend matches everything */

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

@media screen {
  .selector,
  .topLevel { /* ruleset inside media was extended */
    color: blue;
  }
}
@media screen and (min-width: 1023px) {
  .selector,
  .topLevel { /* ruleset inside nested media was extended */
    color: blue;
  }
}

Обнаружение дублирования

В настоящее время не существует обнаружения дублирования.

Пример:

.alert-info,
.widget {
  /* declarations */
}

.alert:extend(.alert-info, .widget) {}

Выходы

.alert-info,
.widget,
.alert,
.alert {
  /* declarations */
}

Случаи использования расширения

Классический вариант использования

Классический вариант использования — избегать добавления базового класса. Например, если у вас есть

.animal {
  background-color: black;
  color: white;
}

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

<a class="animal bear">Bear</a>
.animal {
  background-color: black;
  color: white;
}
.bear {
  background-color: brown;
}

или упростите HTML и используйте расширение в less. например

<a class="bear">Bear</a>
.animal {
  background-color: black;
  color: white;
}
.bear {
  &:extend(.animal);
  background-color: brown;
}

Уменьшение размера CSS

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

Пример — с миксином:

.my-inline-block() {
  display: inline-block;
  font-size: 0;
}
.thing1 {
  .my-inline-block;
}
.thing2 {
  .my-inline-block;
}

Выходы

.thing1 {
  display: inline-block;
  font-size: 0;
}
.thing2 {
  display: inline-block;
  font-size: 0;
}

Пример (с расширениями):

.my-inline-block {
  display: inline-block;
  font-size: 0;
}
.thing1 {
  &:extend(.my-inline-block);
}
.thing2 {
  &:extend(.my-inline-block);
}

Выходы

.my-inline-block,
.thing1,
.thing2 {
  display: inline-block;
  font-size: 0;
}

Объединение стилей / Более продвинутый миксин

Другой вариант использования — это альтернатива примесям: поскольку примеси можно использовать только с простыми селекторами, если у вас есть два разных блока HTML, но вам нужно применить одни и те же стили к обоим, вы можете использовать расширения для связи двух областей.

Пример:

li.list > a {
  // list styles
}
button.list-style {
  &:extend(li.list > a); // use the same list styles
}

Объединение свойств

Открыть оригинал markdown source для "merge"

Объединить свойства

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

Запятая

Добавить значение свойства через запятую

Выпущено v1.5.0

Пример:

.mixin() {
  box-shadow+: inset 0 0 10px #555;
}
.myclass {
  .mixin();
  box-shadow+: 0 0 20px black;
}

Выходы

.myclass {
  box-shadow: inset 0 0 10px #555, 0 0 20px black;
}

Пространство

Добавить значение свойства через пробел

Выпущено v1.7.0

Пример:

.mixin() {
  transform+_: scale(2);
}
.myclass {
  .mixin();
  transform+_: rotate(15deg);
}

Выходы

.myclass {
  transform: scale(2) rotate(15deg);
}

Чтобы избежать непреднамеренных объединений, merge требует явного флага + или +_ в каждом объявлении ожидания соединения.


Миксины

Открыть оригинал markdown source для "mixins"

Подмешивайте свойства из уже существующих стилей

В качестве миксинов можно использовать и классы, и id-селекторы. Например:

.a, #b {
  color: red;
}
.mixin-class {
  .a();
}
.mixin-id {
  #b();
}

Результат:

.a, #b {
  color: red;
}
.mixin-class {
  color: red;
}
.mixin-id {
  color: red;
}

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

.a(); 
.a;    // currently works, but deprecated; don't use
.a (); // white-space before parentheses is also deprecated

Миксины со скобками

Если вы хотите создать миксин, но не хотите видеть его как отдельный селектор в итоговом CSS, добавьте скобки прямо в определение.

.my-mixin {
  color: black;
}
.my-other-mixin() {
  background: white;
}
.class {
  .my-mixin();
  .my-other-mixin();
}

Вывод:

.my-mixin {
  color: black;
}
.class {
  color: black;
  background: white;
}

Селекторы внутри миксинов

Миксин может содержать не только свойства, но и вложенные селекторы.

Например:

.my-hover-mixin() {
  &:hover {
    border: 1px solid red;
  }
}
button {
  .my-hover-mixin();
}

Результат:

button:hover {
  border: 1px solid red;
}

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

Если нужно подмешивать свойства из более сложного селектора, можно "сложить" несколько классов и id в цепочку.

#outer() {
  .inner {
    color: red;
  }
}

.c {
  #outer.inner();
}

Примечание: старый синтаксис Less допускал > и пробел между пространством имен и миксином. Сейчас он считается устаревшим. На данный момент все три записи эквивалентны:

#outer > .inner(); // deprecated
#outer .inner();   // deprecated
#outer.inner();    // preferred

Такое namespacing уменьшает вероятность конфликтов с миксинами из библиотек или пользовательского кода, а заодно помогает логически группировать связанные миксины.

Пример:

#my-library {
  .my-mixin() {
    color: black;
  }
}
// which can be used like this
.class {
  #my-library.my-mixin();
}

Пространства имен с guard-условиями

Если у пространства имен есть guard, содержащиеся в нем миксины будут использоваться только тогда, когда условие guard возвращает true. Guard у namespace вычисляется точно так же, как guard у обычного миксина, поэтому следующие варианты эквивалентны:

#namespace when (@mode = huge) {
  .mixin() { /* */ }
}

#namespace {
  .mixin() when (@mode = huge) { /* */ }
}

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

#sp_1 when (default()) {
  #sp_2 when (default()) {
    .mixin() when not(default()) { /* */ }
  }
}

Ключевое слово !important

Если после вызова миксина написать !important, все свойства, пришедшие из него, будут помечены как !important:

Пример:

.foo (@bg: #f5f5f5, @color: #900) {
  background: @bg;
  color: @color;
}
.unimportant {
  .foo();
}
.important {
  .foo() !important;
}

Результат:

.unimportant {
  background: #f5f5f5;
  color: #900;
}
.important {
  background: #f5f5f5 !important;
  color: #900 !important;
}

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

Открыть оригинал markdown source для "mixins-parametric"

Как передавать аргументы в миксины

Миксины могут принимать аргументы - то есть переменные, которые передаются в блок селекторов в момент подмешивания.

Например:

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

Подмешивать такой миксин можно в разные наборы правил:

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

У параметризованных миксинов могут быть и значения по умолчанию:

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

Тогда вызывать его можно так:

#header {
  .border-radius();
}

И в результате будет использован border-radius: 5px.

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

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

pre { .wrap() }

Вывод:

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

Разделители параметров

Сейчас параметры можно разделять либо точкой с запятой, либо запятой.

Изначально поддерживались только запятые, но позже добавили и ;, чтобы можно было без конфликтов передавать в один аргумент CSS-списки, которые сами содержат запятые.

Примечание: начиная с Less 4.0 список можно оборачивать через paren escape [~()], например .name(@param1: ~(red, blue)). По смыслу это похоже на экранирование кавычек через ~"quote". Во многих кодовых базах это позволяет вообще отказаться от разделения через ;.

Примеры:

  • два аргумента, и в каждом есть список через запятую: .name(1, 2, 3; something, else)
  • три аргумента, каждый с одним числом: .name(1, 2, 3)
  • фиктивная точка с запятой, чтобы передать один аргумент со списком CSS-значений через запятую: .name(1, 2, 3;)
    Если завершающая ; выглядит странно, можно использовать: .name(~(1, 2, 3))
  • способы задать значение по умолчанию со списком через запятую:
    • @param-values: red, blue; .name(@param1: @param-values)
    • .name(@param1: red, blue;)
    • .name(@param1: ~(red, blue))

Перегрузка миксинов

В Less допустимо объявлять несколько миксинов с одинаковым именем и одинаковым количеством параметров. Less использует свойства всех миксинов, которые подходят под вызов. Если, например, вызвать миксин с одним аргументом - .mixin(green); - будут использованы все варианты с ровно одним обязательным параметром:

.mixin(@color) {
  color-1: @color;
}
.mixin(@color, @padding: 2) {
  color-2: @color;
  padding-2: @padding;
}
.mixin(@color, @padding, @margin: 2) {
  color-3: @color;
  padding-3: @padding;
  margin: @margin @margin @margin @margin;
}
.some .selector div {
  .mixin(#008000);
}

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

.some .selector div {
  color-1: #008000;
  color-2: #008000;
  padding-2: 2;
}

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

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

.mixin(@color: black; @margin: 10px; @padding: 20px) {
  color: @color;
  margin: @margin;
  padding: @padding;
}
.class1 {
  .mixin(@margin: 20px; @color: #33acfe);
}
.class2 {
  .mixin(#efca44; @padding: 40px);
}

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

.class1 {
  color: #33acfe;
  margin: 20px;
  padding: 20px;
}
.class2 {
  color: #efca44;
  margin: 10px;
  padding: 40px;
}

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

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

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

Результат:

.big-block {
  -webkit-box-shadow: 2px 5px 1px #000;
     -moz-box-shadow: 2px 5px 1px #000;
          box-shadow: 2px 5px 1px #000;
}

Продвинутые аргументы и переменная @rest

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

.mixin(...) {        // matches 0-N arguments
.mixin() {           // matches exactly 0 arguments
.mixin(@a: 1) {      // matches 0-1 arguments
.mixin(@a: 1, ...) { // matches 0-N arguments
.mixin(@a, ...) {    // matches 1-N arguments

Кроме того:

.mixin(@a, @rest...) {
   // @rest is bound to arguments after @a
   // @arguments is bound to all arguments
}

Сопоставление с образцом

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

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

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

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

.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;
}

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

Что произошло:

  • первое определение миксина не подошло, потому что ожидало dark первым аргументом;
  • второе подошло, потому что ожидало light;
  • третье тоже подошло, потому что принимает любое значение.

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

Совпадение возможно и по числу аргументов. Например:

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

Использование миксинов как функций

Открыть оригинал markdown source для "mixins-as-functions"

Выбор свойств и переменных из вызовов миксина

Аксессоры свойств/значений

Выпущено v3.5.0

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

Пример:

.average(@x, @y) {
  @result: ((@x + @y) / 2);
}

div {
  // call a mixin and look up its "@result" value
  padding: .average(16px, 50px)[@result];
}

Результаты:

div {
  padding: 33px;
}

Переопределение значений примеси

Если у вас есть несколько совпадающих миксинов, все правила оцениваются и объединяются, и возвращается последнее соответствующее значение с этим идентификатором. Это похоже на каскад в CSS и позволяет «переопределить» значения миксина.

// library.less
#library() {
  .mixin() {
    prop: foo;
  }
}

// customize.less
@import "library";
#library() {
  .mixin() {
    prop: bar;
  }
}

.box {
  my-value: #library.mixin[prop];
}

Выходы:

.box {
  my-value: bar;
}

Безымянные запросы

Если вы не укажете искомое значение в [@lookup] и вместо этого напишете [] после вызова миксина или набора правил, все значения будут каскадироваться и будет выбрано последнее объявленное значение.

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

.average(@x, @y) {
  @result: ((@x + @y) / 2);
}

div {
  // call a mixin and look up its final value
  padding: .average(16px, 50px)[];
}

Вывод тот же:

div {
  padding: 33px;
}

Такое же каскадное поведение справедливо для наборов правил или переменных, связанных с вызовами миксинов.

@dr: {
  value: foo;
}
.box {
  my-value: @dr[];
}

Это выводит:

.box {
  my-value: foo;
}

Разблокировка примесей и переменных в области вызывающего объекта

УСТАРЕЛО: используйте средства доступа к свойствам и значениям

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

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

Пример:

.mixin() {
  @width:  100%;
  @height: 200px;
}

.caller {
  .mixin();
  width:  @width;
  height: @height;
}

Результаты:

.caller {
  width:  100%;
  height: 200px;
}

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

.mixin() {
  @size: in-mixin;
  @definedOnlyInMixin: in-mixin;
}

.class {
  margin: @size @definedOnlyInMixin;
  .mixin();
}

@size: globaly-defined-value; // callers parent scope - no protection

Результаты:

.class {
  margin: in-mixin in-mixin;
}

Наконец, миксин, определенный в mixin, также действует как возвращаемое значение:

.unlock(@value) { // outer mixin
  .doSomething() { // nested mixin
    declaration: @value;
  }
}

#namespace {
  .unlock(5); // unlock doSomething mixin
  .doSomething(); //nested mixin was copied here and is usable
}

Результаты:

#namespace {
  declaration: 5;
}

Рекурсивные миксины

Открыть оригинал markdown source для "mixin-loops"

Создание циклов

В Less миксин может вызывать сам себя. Такие рекурсивные миксины в сочетании с guard-условиями и сопоставлением с образцом можно использовать для создания различных итеративных и циклических структур.

Пример:

.loop(@counter) when (@counter > 0) {
  .loop((@counter - 1));    // next iteration
  width: (10px * @counter); // code for each iteration
}

div {
  .loop(5); // launch the loop
}

Выход:

div {
  width: 10px;
  width: 20px;
  width: 30px;
  width: 40px;
  width: 50px;
}

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

.generate-columns(4);

.generate-columns(@n, @i: 1) when (@i =< @n) {
  .column-@{i} {
    width: (@i * 100% / @n);
  }
  .generate-columns(@n, (@i + 1));
}

Выход:

.column-1 {
  width: 25%;
}
.column-2 {
  width: 50%;
}
.column-3 {
  width: 75%;
}
.column-4 {
  width: 100%;
}

Guard-условия миксинов

Открыть оригинал markdown source для "mixin-guards"

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

Пытаясь максимально приблизиться к декларативному характеру 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;
}

Операторы сравнения Guard

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

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

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

.class {
  .truth(40); // Will not match any of the above definitions.
}

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

@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 }

Логические операторы Guard

Вы можете использовать логические операторы с защитой. Синтаксис основан на медиазапросах CSS.

Используйте ключевое слово and для объединения охранников:

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

Вы можете эмулировать оператор или, разделяя охранники запятой ,. Если какой-либо из охранников оценивается как true, это считается совпадением:

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

Используйте ключевое слово not, чтобы отменить условия:

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

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

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

.mixin(@a; @b: 0) when (isnumber(@b)) { ... }
.mixin(@a; @b: black) when (iscolor(@b)) { ... }

Вот основные функции проверки типов:

  • iscolor
  • isnumber
  • isstring
  • iskeyword
  • isurl

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

  • ispixel
  • ispercentage
  • isem
  • isunit

Псевдонимы миксинов

Открыть оригинал markdown source для "mixins-aliasing"

Выпущено v3.5.0

Назначение вызовов миксина переменной

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

#theme.dark.navbar {
  .colors(light) {
    primary: purple;
  }
  .colors(dark) {
    primary: black;
    secondary: grey;
  }
}

.navbar {
  @colors: #theme.dark.navbar.colors(dark);
  background: @colors[primary];
  border: 1px solid @colors[secondary];
}

Это выведет:

.navbar {
  background: black;
  border: 1px solid grey;
}

Вызовы переменных

Целые вызовы миксинов могут иметь псевдонимы и вызываться как вызовы переменных. Как в:

#library() {
  .colors() {
    background: green;
  }
}
.box {
  @alias: #library.colors();
  @alias();
}

Выходы:

.box {
  background: green;
}

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

#library() {
  .colors() {
    background: green;
  }
}
.box {
  @alias: #library.colors;
  @alias();   // ERROR: Could not evaluate variable call @alias
}

Это связано с неоднозначностью, если переменной назначен список селекторов или вызов примеси. Например, в Less 3.5+ эту переменную можно было использовать таким образом.

.box {
  @alias: #library.colors;
  @{alias} {
    a: b;
  }
}

Вышеупомянутое выведет:

.box #library.colors {
  a: b;
}

Отделенные наборы правил

Открыть оригинал markdown source для "detached-rulesets"

Назначьте набор правил переменной

Выпущено v1.7.0

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

Простой пример:

// declare detached ruleset
@detached-ruleset: { background: red; }; // semi-colon is optional in 3.5.0+

// use detached ruleset
.top {
    @detached-ruleset(); 
}

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

.top {
  background: red;
}

Круглые скобки после отдельного вызова набора правил обязательны (за исключением случаев, когда за ними следует искомое значение). Вызов @detached-ruleset; не сработает.

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

.desktop-and-old-ie(@rules) {
  @media screen and (min-width: 1200px) { @rules(); }
  html.lt-ie9 &                         { @rules(); }
}

header {
  background-color: blue;

  .desktop-and-old-ie({
    background-color: red;
  });
}

Здесь миксин desktop-and-old-ie определяет медиа-запрос и корневой класс, чтобы вы могли использовать миксин для переноса фрагмента кода. Это выведет

header {
  background-color: blue;
}
@media screen and (min-width: 1200px) {
  header {
    background-color: red;
  }
}
html.lt-ie9 header {
  background-color: red;
}

Набор правил теперь может быть назначен переменной или передан в миксин и может содержать полный набор функций Less, например.

@my-ruleset: {
    .my-selector {
      background-color: black;
    }
  };

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

@my-ruleset: {
    .my-selector {
      @media tv {
        background-color: black;
      }
    }
  };
@media (orientation:portrait) {
    @my-ruleset();
}

который выведет

@media (orientation: portrait) and tv {
  .my-selector {
    background-color: black;
  }
}

Вызов отдельного набора правил разблокирует (возвращает) все свои примеси в вызывающую сторону так же, как это делают вызовы примесей. Однако он не возвращает переменные.

Возвращенный миксин:

// detached ruleset with a mixin
@detached-ruleset: { 
    .mixin() {
        color: blue;
    }
};
// call detached ruleset
.caller {
    @detached-ruleset(); 
    .mixin();
}

Результаты:

.caller {
  color: blue;
}

Частные переменные:

@detached-ruleset: { 
    @color:blue; // this variable is private
};
.caller {
    color: @color; // syntax error
}

Обзор

Отдельный набор правил может использовать все переменные и примеси, доступные там, где он определен и где он вызван. Другими словами, ему доступны как области определения, так и области вызывающего объекта. Если обе области содержат одну и ту же переменную или примесь, значение области объявления имеет приоритет.

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

Наконец, отдельный набор правил может получить доступ к области действия, если его разблокировать (импортировать) в него.

Примечание: разблокировка переменных в области видимости через вызванный миксин устарела. Используйте способы доступа к свойствам/переменным.

Определение и видимость области вызывающего абонента

Отдельный набор правил видит переменные и примеси вызывающего объекта:

@detached-ruleset: {
  caller-variable: @caller-variable; // variable is undefined here
  .caller-mixin(); // mixin is undefined here
};

selector {
  // use detached ruleset
  @detached-ruleset(); 

  // define variable and mixin needed inside the detached ruleset
  @caller-variable: value;
  .caller-mixin() {
    variable: declaration;
  }
}

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

selector {
  caller-variable: value;
  variable: declaration;
}

Переменные и примеси, доступные из определения, выигрывают у тех, которые доступны в вызывающем коде:

@variable: global;
@detached-ruleset: {
  // will use global variable, because it is accessible
  // from detached-ruleset definition
  variable: @variable; 
};

selector {
  @detached-ruleset();
  @variable: value; // variable defined in caller - will be ignored
}

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

selector {
  variable: global;
}

Reference не изменяет область отдельного набора правил

Набор правил не получает доступа к новым областям просто потому, что на него ссылаются:

@detached-1: { scope-detached: @one @two; };
.one {
  @one: visible;
  .two {
    @detached-2: @detached-1; // copying/renaming ruleset 
    @two: visible; // ruleset can not see this variable
  }
}

.use-place {
  .one > .two(); 
  @detached-2();
}

выдает ошибку:

ERROR 1:32 The variable "@one" was not declared.

Разблокировка будет изменять область действия отдельного набора правил

Отдельный набор правил получает доступ, если его разблокировать (импортировать) внутри области действия:

#space {
  .importer-1() {
    @detached: { scope-detached: @variable; }; // define detached ruleset
  }
}

.importer-2() {
  @variable: value; // unlocked detached ruleset CAN see this variable
  #space > .importer-1(); // unlock/import detached ruleset
}

.use-place {
  .importer-2(); // unlock/import detached ruleset second time
   @detached();
}

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

.use-place {
  scope-detached: value;
}

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

(значения поиска)

Выпущено v3.5.0

Начиная с Less 3.5, вы можете использовать средства доступа к свойствам/переменным (также называемые «поисками») для выбора значения из наборов правил переменных (отсоединенных).

@config: {
  option1: true;
  option2: false;
}

.mixin() when (@config[option1] = true) {
  selected: value;
}

.box {
  .mixin();
}

Выходы:

.box {
  selected: value;
}

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

@config: {
  @colors: {
    primary: blue;
  }
}

.box {
  color: @config[@colors][primary];
}

Переменные переменные в поиске

Возвращаемое значение поиска само по себе может быть переменной. Например, вы можете написать:

@config: {
  @dark: {
    primary: darkblue;
  }
  @light: {
    primary: lightblue;
  }
}

.box {
  @lookup: dark;
  color: @config[@@lookup][primary];
}

Это выведет:

.box {
  color: darkblue;
}

Карты

Открыть оригинал markdown source для "maps"

Выпущено v3.5.0

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

Комбинируя пространство имен с синтаксисом поиска [], вы можете превратить свои наборы правил/миксины в карты.

@sizes: {
  mobile: 320px;
  tablet: 768px;
  desktop: 1024px;
}

.navbar {
  display: block;

  @media (min-width: @sizes[tablet]) {
    display: inline-block;
  }
}

Выходы:

.navbar {
  display: block;
}
@media (min-width: 768px) {
  .navbar {
    display: inline-block;
  }
}

Миксины немного более универсальны, чем карты, из-за пространства имен и возможности перегружать миксины.

#library() {
  .colors() {
    primary: green;
    secondary: blue;
  }
}

#library() {
  .colors() { primary: grey; }
}

.button {
  color: #library.colors[primary];
  border-color: #library.colors[secondary];
}

Выходы:

.button {
  color: grey;
  border-color: blue;
}

Вы также можете упростить это, используя псевдонимы миксинов. То есть:

.button {
  @colors: #library.colors();
  color: @colors[primary];
  border-color: @colors[secondary];
}

Обратите внимание: если искомое значение создает другой набор правил, вы можете добавить второй поиск [], например:

@config: {
  @options: {
    library-on: true
  }
}

& when (@config[@options][library-on] = true) {
  .produce-ruleset {
    prop: val;
  }
}

Таким образом, наборы правил и вызовы переменных могут имитировать тип «пространства имен», аналогичный миксинам.

Что касается использования в качестве карт примесей или наборов правил, назначенных переменным, решать вам. Возможно, вы захотите заменить целые карты, повторно объявив переменную, присвоенную набору правил. Или вы можете захотеть «объединить» отдельные пары ключ/значение, и в этом случае примеси в виде карт могут быть более подходящими.

Использование переменных переменных в поиске

Важно отметить, что значение [@lookup] является именем ключа (переменной) @lookup и не оценивается как переменная. Если вы хотите, чтобы имя ключа было переменным, вы можете использовать синтаксис @@variable.

Например.

.foods() {
  @dessert: ice cream;
}

@key-to-lookup: dessert;

.lunch {
  treat: .foods[@@key-to-lookup];
}

Это выведет:

.lunch {
  treat: ice cream;
}

Область видимости

Открыть оригинал markdown source для "scope"

Дополнительные особенности области видимости в Less.

Особенности области видимости у миксинов

Интуитивно миксины имеют доступ к области видимости, в которой были определены.

#ns {
  @a: one;
  .mixin-1() {
    prop: @a;
  }
}
.rule {
  #ns.mixin-1();
}

/* OUTPUTS:
.rule {
  prop: one;
}
*/

Устаревающие особенности области видимости у миксинов

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

1. (УСТАРЕВАЕТ) Миксины имеют доступ к области видимости вызывающего кода

#ns {
  .mixin-1() {
    prop: @a;
  }
}
.rule {
  @a: one;
  #ns.mixin-1();
}
/* OUTPUTS:
.rule {
  prop: one;
}
*/

Это поведение неинтуитивно по нескольким причинам:

  1. Оно нехарактерно для большинства языков.
  2. По одному только определению миксина невозможно сразу понять, какой результат он даст в конкретном месте вызова.

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

#ns {
  .mixin-1(@a) {
    prop: @a;
  }
}
.rule {
  #ns.mixin-1(@a: one);
}

2. (УСТАРЕВАЕТ) Вызывающая область видимости получает доступ к переменным из миксина

Миксины "проталкивают" свои переменные в область вызова, но только если такая переменная не определена локально.

#ns {
  .mixin-1() {
    @a: one;
    @b: two;
  }
}
.rule {
  @b: three;
  #ns.mixin-1();
  prop-1: @a;
  prop-2: @b;
}
/* OUTPUTS:
.rule {
  prop-1: one;
  prop-2: three;
}
*/

Это тоже трудно назвать очевидным, потому что:

  1. Переменная выше по области видимости может неожиданно быть переопределена или повлиять на результат.
  2. Такое поведение не похоже на обычную семантику большинства языков.
  3. Оно не совпадает с поведением detached rulesets.

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

Предпочтительный подход:

#ns {
  .mixin-1() {
    @a: one;
    @b: two;
  }
}
.rule {
  @returns: #ns.mixin-1();
  prop-1: @returns[@a];
  prop-2: @returns[@b];
}
/* OUTPUTS:
.rule {
  prop-1: one;
  prop-2: two;
}
*/

3. (УСТАРЕВАЕТ) Вызывающая область видимости получает доступ к миксинам из миксина

Похоже на устаревающее поведение переменных: миксины тоже "проталкиваются" наружу в область вызова. Но в отличие от переменных, миксины с одинаковыми именами при слиянии объединяются.

#ns {
  .mixin-1() {
    prop-1: one;
    prop-2: two;
  }
}
.rule {
  #ns();
  .mixin-1();
  .mixin-1() {
    prop-3: three;
  }
}
/* OUTPUT:
.rule {
  prop-1: one;
  prop-2: two;
  prop-3: three;
}
*/

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

#ns {
  .mixin-1() {
    prop-1: one;
    prop-2: two;
  }
}
.rule {
  .mixin-1() {
    prop-3: three;
  }
  #ns.mixin-1();
  .mixin-1();
}
/* OUTPUT:
.rule {
  prop-1: one;
  prop-2: two;
  prop-3: three;
}
*/

Советы и приемы

Источник: less/less.js/issues/1472

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

& {
  // Vars
  @height: 100px;
  @width: 20px;
  // Don't define any prop:value on this scope (as doing so will generate (wrong) output).

  .test {
    height: @height;
    width: @width;
  }
}

.rest {
  height: @height; // Name error: variable @height is undefined
}

Здесь @height и @width существуют только внутри области, созданной конструкцией & { ... }. Внутреннюю область можно создавать и внутри правила:

.some-module {
  @height: 200px;
  @width: 200px;
  text-align: left;
  line-height: @height; // 200px

  & {
    // Override original values
    @height: 100px;
    @width: auto;

    .some-module__element {
      height: @height; // 100px
      width: @width; // 200px
    }

    .some-module__element .text {
      line-height: (@height / 2); // 50px
    }
  }

  & {
    // Override original values
    @height: 50px;

    .some-module__another-element {
      height: @height; // 50px
      width: @width; // 200px
    }

    .some-module__another-element .text {
      line-height: (@height / 2); // 25px
    }
  }
}

CSS guard-условия

Открыть оригинал markdown source для "css-guards"

"if" вокруг селекторов

Выпущено v1.5.0

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

Например, до версии 1.5.0 вам нужно было сделать следующее:

.my-optional-style() when (@my-option = true) {
  button {
    color: white;
  }
}
.my-optional-style();

Теперь вы можете применить защиту непосредственно к стилю.

button when (@my-option = true) {
  color: white;
}

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

& when (@my-option = true) {
  button {
    color: white;
  }
  a {
    color: blue;
  }
}

Обратите внимание, что вы также можете добиться аналогичного шаблона, используя реальную функцию if() и вызов переменной. Как в:

@dr: if(@my-option = true, {
  button {
    color: white;
  }
  a {
    color: blue;
  }
});
@dr();

Директивы @plugin

Открыть оригинал markdown source для "plugins"

Выпущено v2.5.0

Импортируйте плагины JavaScript, чтобы добавить функции и возможности Less.js

Написание вашего первого плагина

Использование at-правила @plugin аналогично использованию @import для файлов .less.

@plugin "my-plugin";  // automatically appends .js if no extension

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

registerPlugin({
    install: function(less, pluginManager, functions) {
        functions.add('pi', function() {
            return Math.PI;
        });
    }
})

или вы можете использовать module.exports (настроено для работы в браузере, а также Node.js).

module.exports = {
    install: function(less, pluginManager, functions) {
        functions.add('pi', function() {
            return Math.PI;
        });
    }
};

Обратите внимание, что другие соглашения Node.js CommonJS, такие как require(), недоступны в браузере. Помните об этом при написании кроссплатформенных плагинов.

Что можно сделать с помощью плагина? Много, но давайте начнем с основ. Сначала мы сосредоточимся на том, что вы можете поместить в функцию install. Допустим, вы пишете это:

// my-plugin.js
install: function(less, pluginManager, functions) {
    functions.add('pi', function() {
        return Math.PI;
    });
}
// etc

Поздравляем! Вы написали плагин Less!

Если бы вы использовали это в своей таблице стилей:

@plugin "my-plugin";
.show-me-pi {
  value: pi();
}

Вы получите:

.show-me-pi {
  value: 3.141592653589793;
}

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

То есть правильнее:

functions.add('pi', function() {
    return new tree.Dimension(Math.PI);
});

Примечание. Размер — это число с единицей измерения или без нее, например «10 пикселей», которое будет выглядеть как less.Dimension(10, "px"). Список модулей см. в Less API.

Теперь вы можете использовать свою функцию в операциях.

@plugin "my-plugin";
.show-me-pi {
  value: pi() * 2;
}

Возможно, вы заметили, что для вашего файла плагина доступны глобальные переменные, а именно объект реестра функций (functions) и объект less. Они там для удобства.

Область плагина

Функции, добавленные с помощью at-правила @plugin, соответствуют правилам области действия Less. Это отлично подходит для авторов библиотек Less, которые хотят добавить функциональность, не создавая конфликтов имен.

Например, предположим, что у вас есть 2 плагина из двух сторонних библиотек, оба из которых имеют функцию с именем «foo».

// lib1.js
// ...
    functions.add('foo', function() {
        return "foo";
    });
// ...

// lib2.js
// ...
    functions.add('foo', function() {
        return "bar";
    });
// ...

Это нормально! Вы можете выбрать, какая функция библиотеки создает какой вывод.

.el-1 {
    @plugin "lib1";
    value: foo();
}
.el-2 {
    @plugin "lib2";
    value: foo();
}

Это произведет:

.el-1 {
    value: foo;
}
.el-2 {
    value: bar;
}

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

.el {
    @plugin "lib1";
}
@value: foo();

Начиная с Less 3.0, функции могут возвращать любой тип Node и могут вызываться на любом уровне.

Это означает, что в версии 2.x это привело бы к ошибке, поскольку функции должны были быть частью значения свойства или присвоения переменной:

.block {
    color: blue;
    my-function-rules();
}

В версии 3.x это уже не так, и функции могут возвращать At-Rules, наборы правил, любой другой узел Less, строки и числа (два последних преобразуются в анонимные узлы).

Null Функции

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

var collection = [];

functions.add('store', function(val) {
    collection.push(val);  // imma store this for later
    return false;
});
@plugin "collections";
@var: 32;
store(@var);

Позже вы можете сделать что-то вроде:

functions.add('retrieve', function(val) {
    return new tree.Value(collection);
});
.get-my-values {
    @plugin "collections";
    values: retrieve();   
}

Объект плагина Less.js

Плагин Less.js должен экспортировать объект, имеющий одно или несколько из этих свойств.

{
    /* Called immediately after the plugin is 
     * first imported, only once. */
    install: function(less, pluginManager, functions) { },

    /* Called for each instance of your @plugin. */
    use: function(context) { },

    /* Called for each instance of your @plugin, 
     * when rules are being evaluated.
     * It's just later in the evaluation lifecycle */
    eval: function(context) { },

    /* Passes an arbitrary string to your plugin 
     * e.g. @plugin (args) "file";
     * This string is not parsed for you, 
     * so it can contain (almost) anything */
    setOptions: function(argumentString) { },

    /* Set a minimum Less compatibility string
     * You can also use an array, as in [3, 0] */
    minVersion: ['3.0'],

    /* Used for lessc only, to explain 
     * options in a Terminal */
    printUsage: function() { },

}

Экземпляр PluginManager для функции install() предоставляет методы для добавления посетителей, файловых менеджеров и постпроцессоров.

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

Предварительно загруженные плагины

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

О том, как это сделать, см. в разделе Предварительно загруженные плагины в разделе «Использование Less.js».