Подробный разбор возможностей языка Less. Если нужен быстрый вводный обзор, начните со страницы Обзор.
Установка, настройка окружения и материалы для разработчиков Less.js собраны в разделе Использование Less.js.
Подробный разбор возможностей языка Less. Если нужен быстрый вводный обзор, начните со страницы Обзор.
Установка, настройка окружения и материалы для разработчиков Less.js собраны в разделе Использование Less.js.
Храните часто используемые значения в одном месте.
В больших стилях одно и то же значение легко может повторяться десятки, если не сотни раз:
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;
}
// Variables
@images: "../img";
// Usage
body {
color: #444;
background: url("@{images}/white-sand.png");
}
@importv1.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-препроцессорах.
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;
}
Обращение к родительскому селектору через
&
Оператор & обозначает родительский селектор внутри вложенного правила. Чаще всего он используется, когда нужно добавить к уже существующему селектору модифицирующий класс или псевдокласс:
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;
}
Импортируйте стили из других файлов.
В обычном CSS директива @import должна идти раньше всех остальных правил. Less к этому ограничению не привязан: @import можно ставить там, где вам удобно.
Пример:
.foo {
background: #900;
}
@import "this-is-valid.less";
Less по-разному обрабатывает @import в зависимости от расширения файла:
.css, он трактуется как обычный CSS, а директива @import остается в итоговом коде как есть. См. также опцию inline ниже..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 есть важное различие в зависимости от способа использования:
@import (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 — это псевдокласс 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;
}
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;
}
В настоящее время :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.
Пример — с миксином:
.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
}
Объединить свойства
Функция 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 требует явного флага + или +_ в каждом объявлении ожидания соединения.
Подмешивайте свойства из уже существующих стилей
В качестве миксинов можно использовать и классы, и 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 возвращает 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;
}
Как передавать аргументы в миксины
Миксины могут принимать аргументы - то есть переменные, которые передаются в блок селекторов в момент подмешивания.
Например:
.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).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);
}
Выбор свойств и переменных из вызовов миксина
Выпущено 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;
}
Создание циклов
В 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%;
}
Охранники полезны, когда вы хотите сопоставить выражения, а не простые значения или арность. Если вы знакомы с функциональным программированием, вы, вероятно, уже сталкивались с ним.
Пытаясь максимально приблизиться к декларативному характеру 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;
}
Полный список операторов сравнения, используемых в 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 }
Вы можете использовать логические операторы с защитой. Синтаксис основан на медиазапросах 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)) { ... }
Вот основные функции проверки типов:
iscolorisnumberisstringiskeywordisurlЕсли вы хотите проверить, находится ли значение в определенной единице измерения, а не в числе, вы можете использовать один из:
ispixelispercentageisemisunitВыпущено 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;
}
Назначьте набор правил переменной
Выпущено 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;
}
Набор правил не получает доступа к новым областям просто потому, что на него ссылаются:
@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;
}
Выпущено 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;
}
Дополнительные особенности области видимости в Less.
Интуитивно миксины имеют доступ к области видимости, в которой были определены.
#ns {
@a: one;
.mixin-1() {
prop: @a;
}
}
.rule {
#ns.mixin-1();
}
/* OUTPUTS:
.rule {
prop: one;
}
*/
Ниже перечислены особенности, которые считаются нежелательными и в будущем могут быть удалены.
#ns {
.mixin-1() {
prop: @a;
}
}
.rule {
@a: one;
#ns.mixin-1();
}
/* OUTPUTS:
.rule {
prop: one;
}
*/
Это поведение неинтуитивно по нескольким причинам:
Предпочтительный подход: передавайте переменную в миксин явно.
#ns {
.mixin-1(@a) {
prop: @a;
}
}
.rule {
#ns.mixin-1(@a: one);
}
Миксины "проталкивают" свои переменные в область вызова, но только если такая переменная не определена локально.
#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;
}
*/
Это тоже трудно назвать очевидным, потому что:
Кроме того, после появления карт значения переменных и миксинов можно забирать явно и напрямую.
Предпочтительный подход:
#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;
}
*/
Похоже на устаревающее поведение переменных: миксины тоже "проталкиваются" наружу в область вызова. Но в отличие от переменных, миксины с одинаковыми именами при слиянии объединяются.
#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
}
}
}
"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();
Выпущено 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, строки и числа (два последних преобразуются в анонимные узлы).
Бывают случаи, когда вы можете вызвать функцию, но вам не нужен какой-либо вывод (например, сохранение значения для последующего использования). В этом случае вам просто нужно вернуть 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 должен экспортировать объект, имеющий одно или несколько из этих свойств.
{
/* 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».