An in-depth guide to features of the LESS language. See the Overview for a quick summary of Less.
For an in-depth guide to installing and setting up a Less environment, as well as documentation on developing for Less, see: Using Less.js.
An in-depth guide to features of the LESS language. See the Overview for a quick summary of Less.
For an in-depth guide to installing and setting up a Less environment, as well as documentation on developing for Less, see: Using Less.js.
Control commonly used values in a single location.
It's not uncommon to see the same value repeated dozens if not hundreds of times across your stylesheets:
a,
.link {
color: #428bca;
}
.widget {
color: #fff;
background: #428bca;
}
Variables make your code easier to maintain by giving you a way to control those values from a single location:
// 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;
}
The examples above focused on using variables to control values in CSS rules, but they can also be used in other places as well, such as selector names, property names, URLs and @import
statements.
v1.4.0
// Variables
@my-selector: banner;
// Usage
.@{my-selector} {
font-weight: bold;
line-height: 40px;
margin: 0 auto;
}
Compiles to:
.banner {
font-weight: bold;
line-height: 40px;
margin: 0 auto;
}
// Variables
@images: "../img";
// Usage
body {
color: #444;
background: url("@{images}/white-sand.png");
}
v1.4.0
Syntax: @import "@{themes}/tidal-wave.less";
Note that before v2.0.0, only variables which have been declared in the root or current scope were considered and that only the current file and calling files were considered when looking for a variable.
Example:
// Variables
@themes: "../../src/themes";
// Usage
@import "@{themes}/tidal-wave.less";
v1.6.0
@property: color;
.widget {
@{property}: #0ee;
background-@{property}: #999;
}
Compiles to:
.widget {
color: #0ee;
background-color: #999;
}
In Less, you can define a variable's name using another variable.
@primary: green;
@secondary: blue;
.section {
@color: primary;
.element {
color: @@color;
}
}
Which compiles to:
.section .element {
color: green;
}
Variables do not have to be declared before being used.
Valid Less snippet:
.lazy-eval {
width: @var;
}
@var: @a;
@a: 9%;
this is valid Less too:
.lazy-eval {
width: @var;
@a: 9%;
}
@var: @a;
@a: 100%;
both compile into:
.lazy-eval {
width: 9%;
}
When defining a variable twice, the last definition of the variable is used, searching from the current scope upwards. This is similar to css itself where the last property inside a definition is used to determine the value.
For instance:
@var: 0;
.class {
@var: 1;
.brass {
@var: 2;
three: @var;
@var: 3;
}
one: @var;
}
Compiles to:
.class {
one: 1;
}
.class .brass {
three: 3;
}
Essentially, each scope has a "final" value, similar to properties in the browser, like this example using custom properties:
.header {
--color: white;
color: var(--color); // the color is black
--color: black;
}
This means that, unlike other CSS pre-processing languages, Less variables behave very much like CSS's.
v3.0.0
You can easily treat properties like variables using the $prop
syntax. Sometimes this can
make your code a little lighter.
.widget {
color: #efefef;
background-color: $color;
}
Compiles to:
.widget {
color: #efefef;
background-color: #efefef;
}
Note that, like variables, Less will choose the last property within the current/parent scope as being the "final" value.
.block {
color: red;
.inner {
background-color: $color;
}
color: blue;
}
Compiles to:
.block {
color: red;
color: blue;
}
.block .inner {
background-color: blue;
}
We sometimes get requests for default variables - an ability to set a variable only if it is not already set. This feature is not required because you can easily override a variable by putting the definition afterwards.
For instance:
// library
@base-color: green;
@dark-color: darken(@base-color, 10%);
// use of library
@import "library.less";
@base-color: red;
This works fine because of Lazy Loading - @base-color
is overridden and @dark-color
is a dark red.
Referencing parent selectors with
&
The &
operator represents the parent selectors of a nested rule and is most commonly used when applying a modifying class or pseudo-class to an existing selector:
a {
color: blue;
&:hover {
color: green;
}
}
results in:
a {
color: blue;
}
a:hover {
color: green;
}
Notice that without the &
, the above example would result in a :hover
rule (a descendant selector that matches hovered elements inside of <a>
tags) and this is not what we typically would want with the nested :hover
.
The "parent selectors" operator has a variety of uses. Basically any time you need the selectors of the nested rules to be combined in other ways than the default. For example another typical use of the &
is to produce repetitive class names:
.button {
&-ok {
background-image: url("ok.png");
}
&-cancel {
background-image: url("cancel.png");
}
&-custom {
background-image: url("custom.png");
}
}
output:
.button-ok {
background-image: url("ok.png");
}
.button-cancel {
background-image: url("cancel.png");
}
.button-custom {
background-image: url("custom.png");
}
&
&
may appear more than once within a selector. This makes it possible to repeatedly refer to a parent selector without repeating its name.
.link {
& + & {
color: red;
}
& & {
color: green;
}
&& {
color: blue;
}
&, &ish {
color: cyan;
}
}
will output:
.link + .link {
color: red;
}
.link .link {
color: green;
}
.link.link {
color: blue;
}
.link, .linkish {
color: cyan;
}
Note that &
represents all parent selectors (not just the nearest ancestor) so the following example:
.grand {
.parent {
& > & {
color: red;
}
& & {
color: green;
}
&& {
color: blue;
}
&, &ish {
color: cyan;
}
}
}
results in:
.grand .parent > .grand .parent {
color: red;
}
.grand .parent .grand .parent {
color: green;
}
.grand .parent.grand .parent {
color: blue;
}
.grand .parent,
.grand .parentish {
color: cyan;
}
It can be useful to prepend a selector to the inherited (parent) selectors. This can be done by putting the &
after current selector.
For example, when using Modernizr, you might want to specify different rules based on supported features:
.header {
.menu {
border-radius: 5px;
.no-borderradius & {
background-image: url('images/button-background.png');
}
}
}
The selector .no-borderradius &
will prepend .no-borderradius
to its parent .header .menu
to form the.no-borderradius .header .menu
on output:
.header .menu {
border-radius: 5px;
}
.no-borderradius .header .menu {
background-image: url('images/button-background.png');
}
&
can also be used to generate every possible permutation of selectors in a comma separated list:
p, a, ul, li {
border-top: 2px dotted #366;
& + & {
border-top: 0;
}
}
This expands to all possible (16) combinations of the specified elements:
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 styles from other style sheets
In standard CSS, @import
at-rules must precede all other types of rules. But Less doesn't care where you put @import
statements.
Example:
.foo {
background: #900;
}
@import "this-is-valid.less";
@import
statements may be treated differently by Less depending on the file extension:
.css
extension it will be treated as CSS and the @import
statement left as-is (see the inline option below)..less
will be appended and it will be included as a imported Less file.Examples:
@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
The following options can be used to override this behavior.
Less offers several extensions to the CSS
@import
CSS at-rule to provide more flexibility over what you can do with external files.
Syntax: @import (keyword) "filename";
The following import options have been implemented:
reference
: use a Less file but do not output itinline
: include the source file in the output but do not process itless
: treat the file as a Less file, no matter what the file extensioncss
: treat the file as a CSS file, no matter what the file extensiononce
: only include the file once (this is default behavior)multiple
: include the file multiple timesoptional
: continue compiling when file is not foundMore than one keyword per
@import
is allowed, you will have to use commas to separate the keywords:
Example: @import (optional, reference) "foo.less";
Use @import (reference)
to import external files, but without adding the imported styles to the compiled output unless referenced.
Released v1.5.0
Example: @import (reference) "foo.less";
Imagine that reference
marks every at-rule and selector with a reference flag in the imported file, imports as normal, but when the CSS is generated, "reference" selectors (as well as any media queries containing only reference selectors) are not output. reference
styles will not show up in your generated CSS unless the reference styles are used as mixins or extended.
Additionally, reference
produces different results depending on which method was used (mixin or extend):
@import
statement.reference
style is used as an implicit mixin, its rules are mixed-in, marked "not reference", and appear in the referenced place as normal.This allows you to pull in only specific, targeted styles from a library such as Bootstrap by doing something like this:
.navbar:extend(.navbar all) {}
And you will pull in only .navbar
related styles from Bootstrap.
Use @import (inline)
to include external files, but not process them.
Released v1.5.0
Example: @import (inline) "not-less-compatible.css";
You will use this when a CSS file may not be Less compatible; this is because although Less supports most known standards CSS, it does not support comments in some places and does not support all known CSS hacks without modifying the CSS.
So you can use this to include the file in the output so that all CSS will be in one file.
Use @import (less)
to treat imported files as Less, regardless of file extension.
Released v1.4.0
Example:
@import (less) "foo.css";
Use @import (css)
to treat imported files as regular CSS, regardless of file extension. This means the import statement will be left as it is.
Released v1.4.0
Example:
@import (css) "foo.less";
outputs
@import "foo.less";
The default behavior of @import
statements. It means the file is imported only once and subsequent import statements for that file will be ignored.
Released v1.4.0
This is the default behavior of @import
statements.
Example:
@import (once) "foo.less";
@import (once) "foo.less"; // this statement will be ignored
Use @import (multiple)
to allow importing of multiple files with the same name. This is the opposite behavior to once.
Released v1.4.0
Example:
// file: foo.less
.a {
color: green;
}
// file: main.less
@import (multiple) "foo.less";
@import (multiple) "foo.less";
Outputs
.a {
color: green;
}
.a {
color: green;
}
Use @import (optional)
to allow importing of a file only when it exists. Without the optional
keyword Less throws a FileError and stops compiling when importing a file that can not be found.
Released v2.3.0
Extend is a Less pseudo-class which merges the selector it is put on with ones that match what it references.
Released v1.4.0
nav ul {
&:extend(.inline);
background: blue;
}
In the rule set above, the :extend
selector will apply the "extending selector" (nav ul
) onto the .inline
class wherever the .inline
class appears. The declaration block will be kept as-is, but without any reference to the extend (because extend isn't css).
So the following:
nav ul {
&:extend(.inline);
background: blue;
}
.inline {
color: red;
}
Outputs
nav ul {
background: blue;
}
.inline,
nav ul {
color: red;
}
Notice how the nav ul:extend(.inline)
selector gets output as nav ul
- the extend gets removed before output and the selector block left as-is. If no properties are put in that block then it gets removed from the output (but the extend still may affect other selectors).
The extend is either attached to a selector or placed into a ruleset. It looks like a pseudo-class with selector parameter optionally followed by the keyword all
:
Example:
.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"
}
It can contain one or more classes to extend, separated by commas.
Example:
.e:extend(.f) {}
.e:extend(.g) {}
// the above and the below do the same thing
.e:extend(.f, .g) {}
Extend attached to a selector looks like an ordinary pseudo-class with selector as a parameter. A selector can contain multiple extend clauses, but all extends must be at the end of the selector.
pre:hover:extend(div pre)
.pre:hover :extend(div pre)
.pre:hover:extend(div pre):extend(.bucket tr)
- Note this is the same as pre:hover:extend(div pre, .bucket tr)
pre:hover:extend(div pre).nth-child(odd)
. Extend must be last.If a ruleset contains multiple selectors, any of them can have the extend keyword. Multiple selectors with extend in one ruleset:
.big-division,
.big-bag:extend(.bag),
.big-bucket:extend(.bucket) {
// body
}
Extend can be placed into a ruleset's body using &:extend(selector)
syntax. Placing extend into a body is a shortcut for placing it into every single selector of that ruleset.
Extend inside a body:
pre:hover,
.some-class {
&:extend(div pre);
}
is exactly the same as adding an extend after each selector:
pre:hover:extend(div pre),
.some-class:extend(div pre) {}
Extend is able to match nested selectors. Following less:
Example:
.bucket {
tr { // nested ruleset with target selector
color: blue;
}
}
.some-class:extend(.bucket tr) {} // nested ruleset is recognized
Outputs
.bucket tr,
.some-class {
color: blue;
}
Essentially the extend looks at the compiled css, not the original less.
Example:
.bucket {
tr & { // nested ruleset with target selector
color: blue;
}
}
.some-class:extend(tr .bucket) {} // nested ruleset is recognized
Outputs
tr .bucket,
.some-class {
color: blue;
}
Extend by default looks for exact match between selectors. It does matter whether selector uses leading star or not. It does not matter that two nth-expressions have the same meaning, they need to have to same form in order to be matched. The only exception are quotes in attribute selector, less knows they have the same meaning and matches them.
Example:
.a.class,
.class.a,
.class > .a {
color: blue;
}
.test:extend(.class) {} // this will NOT match the any selectors above
Leading star does matter. Selectors *.class
and .class
are equivalent, but extend will not match them:
*.class {
color: blue;
}
.noStar:extend(.class) {} // this will NOT match the *.class selector
Outputs
*.class {
color: blue;
}
Order of pseudo-classes does matter. Selectors link:hover:visited
and link:visited:hover
match the same set of elements, but extend treats them as different:
link:hover:visited {
color: blue;
}
.selector:extend(link:visited:hover) {}
Outputs
link:hover:visited {
color: blue;
}
Nth expression form does matter. Nth-expressions 1n+3
and n+3
are equivalent, but extend will not match them:
:nth-child(1n+3) {
color: blue;
}
.child:extend(:nth-child(n+3)) {}
Outputs
:nth-child(1n+3) {
color: blue;
}
Quote type in attribute selector does not matter. All of the following are equivalent.
[title=identifier] {
color: blue;
}
[title='identifier'] {
color: blue;
}
[title="identifier"] {
color: blue;
}
.noQuote:extend([title=identifier]) {}
.singleQuote:extend([title='identifier']) {}
.doubleQuote:extend([title="identifier"]) {}
Outputs
[title=identifier],
.noQuote,
.singleQuote,
.doubleQuote {
color: blue;
}
[title='identifier'],
.noQuote,
.singleQuote,
.doubleQuote {
color: blue;
}
[title="identifier"],
.noQuote,
.singleQuote,
.doubleQuote {
color: blue;
}
When you specify the all keyword last in an extend argument it tells Less to match that selector as part of another selector. The selector will be copied and the matched part of the selector only will then be replaced with the extend, making a new selector.
Example:
.a.b.test,
.test.c {
color: orange;
}
.test {
&:hover {
color: green;
}
}
.replacement:extend(.test all) {}
Outputs
.a.b.test,
.test.c,
.a.b.replacement,
.replacement.c {
color: orange;
}
.test:hover,
.replacement:hover {
color: green;
}
You can think of this mode of operation as essentially doing a non-destructive search and replace.
Extend is not able to match selectors with variables. If selector contains variable, extend will ignore it.
However, extend can be attached to interpolated selector.
Selector with variable will not be matched:
@variable: .bucket;
@{variable} { // interpolated selector
color: blue;
}
.some-class:extend(.bucket) {} // does nothing, no match is found
and extend with variable in target selector matches nothing:
.bucket {
color: blue;
}
.some-class:extend(@{variable}) {} // interpolated selector matches nothing
@variable: .bucket;
Both of the above examples compile into:
.bucket {
color: blue;
}
However, :extend
attached to an interpolated selector works:
.bucket {
color: blue;
}
@{variable}:extend(.bucket) {}
@variable: .selector;
compiles to:
.bucket, .selector {
color: blue;
}
Currently, an :extend
inside a @media
declaration will only match selectors inside the same media declaration:
@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;
}
}
compiles into:
@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;
}
}
Note: extending does not match selectors inside a nested @media
declaration:
@media screen {
.screenClass:extend(.selector) {} // extend inside media
@media (min-width: 1023px) {
.selector { // ruleset inside nested media - extend ignores it
color: blue;
}
}
}
This compiles into:
@media screen and (min-width: 1023px) {
.selector { /* ruleset inside another nested media was ignored */
color: blue;
}
}
Top level extend matches everything including selectors inside nested media:
@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 */
compiles into:
@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;
}
}
Currently there is no duplication detection.
Example:
.alert-info,
.widget {
/* declarations */
}
.alert:extend(.alert-info, .widget) {}
Outputs
.alert-info,
.widget,
.alert,
.alert {
/* declarations */
}
The classic use case is to avoid adding a base class. For example, if you have
.animal {
background-color: black;
color: white;
}
and you want to have a subtype of animal which overrides the background color then you have two options, firstly change your HTML
<a class="animal bear">Bear</a>
.animal {
background-color: black;
color: white;
}
.bear {
background-color: brown;
}
or have simplified html and use extend in your less. e.g.
<a class="bear">Bear</a>
.animal {
background-color: black;
color: white;
}
.bear {
&:extend(.animal);
background-color: brown;
}
Mixins copy all of the properties into a selector, which can lead to unnecessary duplication. Therefore you can use extends instead of mixins to move the selector up to the properties you wish to use, which leads to less CSS being generated.
Example - with mixin:
.my-inline-block() {
display: inline-block;
font-size: 0;
}
.thing1 {
.my-inline-block;
}
.thing2 {
.my-inline-block;
}
Outputs
.thing1 {
display: inline-block;
font-size: 0;
}
.thing2 {
display: inline-block;
font-size: 0;
}
Example (with extends):
.my-inline-block {
display: inline-block;
font-size: 0;
}
.thing1 {
&:extend(.my-inline-block);
}
.thing2 {
&:extend(.my-inline-block);
}
Outputs
.my-inline-block,
.thing1,
.thing2 {
display: inline-block;
font-size: 0;
}
Another use-case is as an alternative for a mixin - because mixins can only be used with simple selectors, if you have two different blocks of html, but need to apply the same styles to both you can use extends to relate two areas.
Example:
li.list > a {
// list styles
}
button.list-style {
&:extend(li.list > a); // use the same list styles
}
Combine properties
The merge
feature allows for aggregating values from multiple properties into a comma or space separated list under a single property. merge
is useful for properties such as background and transform.
Append property value with comma
Released v1.5.0
Example:
.mixin() {
box-shadow+: inset 0 0 10px #555;
}
.myclass {
.mixin();
box-shadow+: 0 0 20px black;
}
Outputs
.myclass {
box-shadow: inset 0 0 10px #555, 0 0 20px black;
}
Append property value with space
Released v1.7.0
Example:
.mixin() {
transform+_: scale(2);
}
.myclass {
.mixin();
transform+_: rotate(15deg);
}
Outputs
.myclass {
transform: scale(2) rotate(15deg);
}
To avoid any unintentional joins, merge
requires an explicit +
or +_
flag on each join pending declaration.
"mix-in" properties from existing styles
You can mix-in class selectors and id selectors, e.g.
.a, #b {
color: red;
}
.mixin-class {
.a();
}
.mixin-id {
#b();
}
which results in:
.a, #b {
color: red;
}
.mixin-class {
color: red;
}
.mixin-id {
color: red;
}
Historically, the parentheses in a mixin call are optional, but optional parentheses are deprecated and will be required in a future release.
.a();
.a; // currently works, but deprecated; don't use
.a (); // white-space before parentheses is also deprecated
If you want to create a mixin but you do not want that mixin to be in your CSS output, put parentheses after the mixin definition.
.my-mixin {
color: black;
}
.my-other-mixin() {
background: white;
}
.class {
.my-mixin();
.my-other-mixin();
}
outputs
.my-mixin {
color: black;
}
.class {
color: black;
background: white;
}
Mixins can contain more than just properties, they can contain selectors too.
For example:
.my-hover-mixin() {
&:hover {
border: 1px solid red;
}
}
button {
.my-hover-mixin();
}
Outputs
button:hover {
border: 1px solid red;
}
If you want to mixin properties inside a more complicated selector, you can stack up multiple ids or classes.
#outer() {
.inner {
color: red;
}
}
.c {
#outer.inner();
}
Note: legacy Less syntax allows >
and whitespace between namespaces and mixins. This syntax is deprecated and may be removed. Currently, these do the same thing.
#outer > .inner(); // deprecated
#outer .inner(); // deprecated
#outer.inner(); // preferred
Namespacing your mixins like this reduces conflicts with other library mixins or user mixins, but can also be a way to "organize" groups of mixins.
Example:
#my-library {
.my-mixin() {
color: black;
}
}
// which can be used like this
.class {
#my-library.my-mixin();
}
If a namespace has a guard, mixins defined by it are used only if the guard condition returns true. A namespace guard is evaluated exactly the same as a guard on a mixin, so the following two mixins work the same way:
#namespace when (@mode = huge) {
.mixin() { /* */ }
}
#namespace {
.mixin() when (@mode = huge) { /* */ }
}
The default
function is assumed to have the same value for all nested namespaces and mixin. The following mixin is never evaluated; one of its guards is guaranteed to be false:
#sp_1 when (default()) {
#sp_2 when (default()) {
.mixin() when not(default()) { /* */ }
}
}
!important
keywordUse the !important
keyword after mixin call to mark all properties inherited by it as !important
:
Example:
.foo (@bg: #f5f5f5, @color: #900) {
background: @bg;
color: @color;
}
.unimportant {
.foo();
}
.important {
.foo() !important;
}
Results in:
.unimportant {
background: #f5f5f5;
color: #900;
}
.important {
background: #f5f5f5 !important;
color: #900 !important;
}
How to pass arguments to mixins
Mixins can also take arguments, which are variables passed to the block of selectors when it is mixed in.
For example:
.border-radius(@radius) {
-webkit-border-radius: @radius;
-moz-border-radius: @radius;
border-radius: @radius;
}
And here's how we can mix it into various rulesets:
#header {
.border-radius(4px);
}
.button {
.border-radius(6px);
}
Parametric mixins can also have default values for their parameters:
.border-radius(@radius: 5px) {
-webkit-border-radius: @radius;
-moz-border-radius: @radius;
border-radius: @radius;
}
We can invoke it like this now:
#header {
.border-radius();
}
And it will include a 5px border-radius.
You can also use parametric mixins which don't take parameters. This is useful if you want to hide the ruleset from the CSS output, but want to include its properties in other rulesets:
.wrap() {
text-wrap: wrap;
white-space: -moz-pre-wrap;
white-space: pre-wrap;
word-wrap: break-word;
}
pre { .wrap() }
Which would output:
pre {
text-wrap: wrap;
white-space: -moz-pre-wrap;
white-space: pre-wrap;
word-wrap: break-word;
}
Parameters are currently either semicolon or comma separated.
Originally, parameters were only separated by commas, but the semi-colon was later added to support passing comma-separated list values to single arguments.
Note: As of Less 4.0, you can wrap a list value using a paren escape [~()
], e.g. .name(@param1: ~(red, blue))
. This is similar to the quote escape syntax: ~"quote"
. This may make semi-colon separators un-necessary in your code-base.
Examples:
.name(1, 2, 3; something, else)
.name(1, 2, 3)
.name(1, 2, 3;)
. Note: if the trailing semi-colon seems strange, you may prefer: .name(~(1, 2, 3))
@param-values: red, blue; .name(@param1: @param-values)
..name(@param1: red, blue;)
.name(@param1: ~(red, blue))
It is legal to define multiple mixins with the same name and number of parameters. Less will use properties of all that can apply. If you used the mixin with one parameter e.g. .mixin(green);
, then properties of all mixins with exactly one mandatory parameter will be used:
.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);
}
compiles into:
.some .selector div {
color-1: #008000;
color-2: #008000;
padding-2: 2;
}
A mixin reference can supply parameters values by their names instead of just positions. Any parameter can be referenced by its name and they do not have to be in any special order:
.mixin(@color: black; @margin: 10px; @padding: 20px) {
color: @color;
margin: @margin;
padding: @padding;
}
.class1 {
.mixin(@margin: 20px; @color: #33acfe);
}
.class2 {
.mixin(#efca44; @padding: 40px);
}
compiles into:
.class1 {
color: #33acfe;
margin: 20px;
padding: 20px;
}
.class2 {
color: #efca44;
margin: 10px;
padding: 40px;
}
@arguments
Variable@arguments
has a special meaning inside mixins, it contains all the arguments passed, when the mixin was called. This is useful if you don't want to deal with individual parameters:
.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);
}
Which results in:
.big-block {
-webkit-box-shadow: 2px 5px 1px #000;
-moz-box-shadow: 2px 5px 1px #000;
box-shadow: 2px 5px 1px #000;
}
@rest
VariableYou can use ...
if you want your mixin to take a variable number of arguments. Using this after a variable name will assign those arguments to the variable.
.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
Furthermore:
.mixin(@a, @rest...) {
// @rest is bound to arguments after @a
// @arguments is bound to all arguments
}
Sometimes, you may want to change the behavior of a mixin, based on the parameters you pass to it. Let's start with something basic:
.mixin(@s, @color) { ... }
.class {
.mixin(@switch, #888);
}
Now let's say we want .mixin
to behave differently, based on the value of @switch
, we could define .mixin
as such:
.mixin(dark, @color) {
color: darken(@color, 10%);
}
.mixin(light, @color) {
color: lighten(@color, 10%);
}
.mixin(@_, @color) {
display: block;
}
Now, if we run:
@switch: light;
.class {
.mixin(@switch, #888);
}
We will get the following CSS:
.class {
color: #a2a2a2;
display: block;
}
Where the color passed to .mixin
was lightened. If the value of @switch
was dark
,
the result would be a darker color.
Here's what happened:
dark
as the first argument.light
.Only mixin definitions which matched were used. Variables match and bind to any value. Anything other than a variable matches only with a value equal to itself.
We can also match on arity, here's an example:
.mixin(@a) {
color: @a;
}
.mixin(@a, @b) {
color: fade(@a, @b);
}
Now if we call .mixin
with a single argument, we will get the output of the first definition,
but if we call it with two arguments, we will get the second definition, namely @a
faded to @b
.
Selecting properties and variables from mixin calls
Released v3.5.0
Starting in Less 3.5, you can use property/variable accessors to select a value from an evaluated mixin's rules. This can allow you to use mixins similar to functions.
Example:
.average(@x, @y) {
@result: ((@x + @y) / 2);
}
div {
// call a mixin and look up its "@result" value
padding: .average(16px, 50px)[@result];
}
Results in:
div {
padding: 33px;
}
If you have multiple matching mixins, all rules are evaluated and merged, and the last matching value with that identifier is returned. This is similar to the cascade in CSS, and it allows you to "override" mixin values.
// library.less
#library() {
.mixin() {
prop: foo;
}
}
// customize.less
@import "library";
#library() {
.mixin() {
prop: bar;
}
}
.box {
my-value: #library.mixin[prop];
}
Outputs:
.box {
my-value: bar;
}
If you don't specify a lookup value in [@lookup]
and instead write []
after a mixin or ruleset call, all values will cascade and the last declared value will be selected.
Meaning: the averaging mixin from the above example could be written as:
.average(@x, @y) {
@result: ((@x + @y) / 2);
}
div {
// call a mixin and look up its final value
padding: .average(16px, 50px)[];
}
The output is the same:
div {
padding: 33px;
}
The same cascading behavior is true for rulesets or variables aliased to mixin calls.
@dr: {
value: foo;
}
.box {
my-value: @dr[];
}
This outputs:
.box {
my-value: foo;
}
DEPRECATED - Use Property / Value Accessors
Variables and mixins defined in a mixin are visible and can be used in caller's scope. There is only one exception: a variable is not copied if the caller contains a variable with the same name (that includes variables defined by another mixin call). Only variables present in callers local scope are protected. Variables inherited from parent scopes are overridden.
Note: this behavior is deprecated, and in the future, variables and mixins will not be merged into the caller scope in this way.
Example:
.mixin() {
@width: 100%;
@height: 200px;
}
.caller {
.mixin();
width: @width;
height: @height;
}
Results in:
.caller {
width: 100%;
height: 200px;
}
Variables defined directly in callers scope cannot be overridden. However, variables defined in callers parent scope is not protected and will be overridden:
.mixin() {
@size: in-mixin;
@definedOnlyInMixin: in-mixin;
}
.class {
margin: @size @definedOnlyInMixin;
.mixin();
}
@size: globaly-defined-value; // callers parent scope - no protection
Results in:
.class {
margin: in-mixin in-mixin;
}
Finally, mixin defined in mixin acts as return value too:
.unlock(@value) { // outer mixin
.doSomething() { // nested mixin
declaration: @value;
}
}
#namespace {
.unlock(5); // unlock doSomething mixin
.doSomething(); //nested mixin was copied here and is usable
}
Results in:
#namespace {
declaration: 5;
}
Creating loops
In Less a mixin can call itself. Such recursive mixins, when combined with Guard Expressions and Pattern Matching, can be used to create various iterative/loop structures.
Example:
.loop(@counter) when (@counter > 0) {
.loop((@counter - 1)); // next iteration
width: (10px * @counter); // code for each iteration
}
div {
.loop(5); // launch the loop
}
Output:
div {
width: 10px;
width: 20px;
width: 30px;
width: 40px;
width: 50px;
}
A generic example of using a recursive loop to generate CSS grid classes:
.generate-columns(4);
.generate-columns(@n, @i: 1) when (@i =< @n) {
.column-@{i} {
width: (@i * 100% / @n);
}
.generate-columns(@n, (@i + 1));
}
Output:
.column-1 {
width: 25%;
}
.column-2 {
width: 50%;
}
.column-3 {
width: 75%;
}
.column-4 {
width: 100%;
}
Guards are useful when you want to match on expressions, as opposed to simple values or arity. If you are familiar with functional programming, you have probably encountered them already.
In trying to stay as close as possible to the declarative nature of CSS, Less has opted to implement conditional execution via guarded mixins instead of if
/else
statements, in the vein of @media
query feature specifications.
Let's start with an example:
.mixin(@a) when (lightness(@a) >= 50%) {
background-color: black;
}
.mixin(@a) when (lightness(@a) < 50%) {
background-color: white;
}
.mixin(@a) {
color: @a;
}
The key is the when
keyword, which introduces a guard sequence (here with only one guard). Now if we run the following code:
.class1 { .mixin(#ddd) }
.class2 { .mixin(#555) }
Here's what we'll get:
.class1 {
background-color: black;
color: #ddd;
}
.class2 {
background-color: white;
color: #555;
}
The full list of comparison operators usable in guards are: >
, >=
, =
, =<
, <
. Additionally, the keyword true
is the only truthy value, making these two mixins equivalent:
.truth(@a) when (@a) { ... }
.truth(@a) when (@a = true) { ... }
Any value other than the keyword true
is falsy:
.class {
.truth(40); // Will not match any of the above definitions.
}
Note that you can also compare arguments with each other, or with non-arguments:
@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 }
You can use logical operators with guards. The syntax is based on CSS media queries.
Use the and
keyword to combine guards:
.mixin(@a) when (isnumber(@a)) and (@a > 0) { ... }
You can emulate the or operator by separating guards with a comma ,
. If any of the guards evaluate to true, it's considered a match:
.mixin(@a) when (@a > 10), (@a < -10) { ... }
Use the not
keyword to negate conditions:
.mixin(@b) when not (@b > 0) { ... }
Lastly, if you want to match mixins based on value type, you can use the is
functions:
.mixin(@a; @b: 0) when (isnumber(@b)) { ... }
.mixin(@a; @b: black) when (iscolor(@b)) { ... }
Here are the basic type checking functions:
iscolor
isnumber
isstring
iskeyword
isurl
If you want to check if a value is in a specific unit in addition to being a number, you may use one of:
ispixel
ispercentage
isem
isunit
Released v3.5.0
Assigning mixin calls to a variable
Mixins can be assigned to a variable to be called as a variable call, or can be used for map lookup.
#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];
}
This would output:
.navbar {
background: black;
border: 1px solid grey;
}
Entire mixin calls can be aliased and called as variable calls. As in:
#library() {
.colors() {
background: green;
}
}
.box {
@alias: #library.colors();
@alias();
}
Outputs:
.box {
background: green;
}
Note, unlike mixins used in root, mixin calls assigned to variables and called with no arguments always require parentheses. The following is not valid.
#library() {
.colors() {
background: green;
}
}
.box {
@alias: #library.colors;
@alias(); // ERROR: Could not evaluate variable call @alias
}
This is because it's ambiguous if variable is assigned a list of selectors or a mixin call. For example, in Less 3.5+, this variable could be used this way.
.box {
@alias: #library.colors;
@{alias} {
a: b;
}
}
The above would output:
.box #library.colors {
a: b;
}
Assign a ruleset to a variable
Released v1.7.0
A detached ruleset is a group of css properties, nested rulesets, media declarations or anything else stored in a variable. You can include it into a ruleset or another structure and all its properties are going to be copied there. You can also use it as a mixin argument and pass it around as any other variable.
Simple example:
// declare detached ruleset
@detached-ruleset: { background: red; }; // semi-colon is optional in 3.5.0+
// use detached ruleset
.top {
@detached-ruleset();
}
compiles into:
.top {
background: red;
}
Parentheses after a detached ruleset call are mandatory (except when followed by a lookup value). The call @detached-ruleset;
would not work.
It is useful when you want to define a mixin that abstracts out either wrapping a piece of code in a media query or a non-supported browser class name. The rulesets can be passed to mixin so that the mixin can wrap the content, e.g.
.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;
});
}
Here the desktop-and-old-ie
mixin defines the media query and root class so that you can use a mixin to wrap a piece of code. This will output
header {
background-color: blue;
}
@media screen and (min-width: 1200px) {
header {
background-color: red;
}
}
html.lt-ie9 header {
background-color: red;
}
A ruleset can be now assigned to a variable or passed in to a mixin and can contain the full set of Less features, e.g.
@my-ruleset: {
.my-selector {
background-color: black;
}
};
You can even take advantage of media query bubbling, for instance
@my-ruleset: {
.my-selector {
@media tv {
background-color: black;
}
}
};
@media (orientation:portrait) {
@my-ruleset();
}
which will output
@media (orientation: portrait) and tv {
.my-selector {
background-color: black;
}
}
A detached ruleset call unlocks (returns) all its mixins into caller the same way as mixin calls do. However, it does not return variables.
Returned mixin:
// detached ruleset with a mixin
@detached-ruleset: {
.mixin() {
color: blue;
}
};
// call detached ruleset
.caller {
@detached-ruleset();
.mixin();
}
Results in:
.caller {
color: blue;
}
Private variables:
@detached-ruleset: {
@color:blue; // this variable is private
};
.caller {
color: @color; // syntax error
}
A detached ruleset can use all variables and mixins accessible where it is defined and where it is called. Otherwise said, both definition and caller scopes are available to it. If both scopes contains the same variable or mixin, declaration scope value takes precedence.
Declaration scope is the one where detached ruleset body is defined. Copying a detached ruleset from one variable into another cannot modify its scope. The ruleset does not gain access to new scopes just by being referenced there.
Lastly, a detached ruleset can gain access to scope by being unlocked (imported) into it.
Note: unlocking variables into scope via a called mixin is deprecated. Use property / variable accessors.
A detached ruleset sees the caller's variables and mixins:
@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;
}
}
compiles into:
selector {
caller-variable: value;
variable: declaration;
}
Variable and mixins accessible from definition win over those available in the caller:
@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
}
compiles into:
selector {
variable: global;
}
A ruleset does not gain access to new scopes just by being referenced there:
@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();
}
throws an error:
ERROR 1:32 The variable "@one" was not declared.
A detached ruleset gains access by being unlocked (imported) inside a scope:
#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();
}
compiles into:
.use-place {
scope-detached: value;
}
Released v3.5.0
Starting in Less 3.5, you can use property/variable accessors (also called "lookups") to select a value from variable (detached) rulesets.
@config: {
option1: true;
option2: false;
}
.mixin() when (@config[option1] = true) {
selected: value;
}
.box {
.mixin();
}
Outputs:
.box {
selected: value;
}
If what is returned from a lookup is another detached ruleset, you can use a second lookup to get that value.
@config: {
@colors: {
primary: blue;
}
}
.box {
color: @config[@colors][primary];
}
The lookup value that is returned can itself be variable. As in, you can write:
@config: {
@dark: {
primary: darkblue;
}
@light: {
primary: lightblue;
}
}
.box {
@lookup: dark;
color: @config[@@lookup][primary];
}
This will output:
.box {
color: darkblue;
}
Released v3.5.0
Use rulesets and mixins as maps of values
By combining namespacing with the lookup []
syntax, you can turn your rulesets / mixins into maps.
@sizes: {
mobile: 320px;
tablet: 768px;
desktop: 1024px;
}
.navbar {
display: block;
@media (min-width: @sizes[tablet]) {
display: inline-block;
}
}
Outputs:
.navbar {
display: block;
}
@media (min-width: 768px) {
.navbar {
display: inline-block;
}
}
Mixins are a little more versatile as maps because of namespacing and the ability to overload mixins.
#library() {
.colors() {
primary: green;
secondary: blue;
}
}
#library() {
.colors() { primary: grey; }
}
.button {
color: #library.colors[primary];
border-color: #library.colors[secondary];
}
Outputs:
.button {
color: grey;
border-color: blue;
}
You can also make this easier by aliasing mixins. That is:
.button {
@colors: #library.colors();
color: @colors[primary];
border-color: @colors[secondary];
}
Note, if a lookup value produces another ruleset, you can append a second []
lookup, as in:
@config: {
@options: {
library-on: true
}
}
& when (@config[@options][library-on] = true) {
.produce-ruleset {
prop: val;
}
}
In this way, rulesets and variable calls can emulate a type of "namespacing", similar to mixins.
As far as whether to use mixins or rulesets assigned to variables as maps, it's up to you. You may want to replace entire maps by re-declaring a variable assigned to a rulset. Or you may want to "merge" individual key/value pairs, in which case mixins as maps might be more appropriate.
One important thing to notice is that the value in [@lookup]
is the key (variable) name @lookup
, and is not evaluated as a variable. If you want the key name itself to be variable, you can use the @@variable
syntax.
E.g.
.foods() {
@dessert: ice cream;
}
@key-to-lookup: dessert;
.lunch {
treat: .foods[@@key-to-lookup];
}
This would output:
.lunch {
treat: ice cream;
}
Some additional scoping features of Less
Intuitively, mixins have access to definition scope.
#ns {
@a: one;
.mixin-1() {
prop: @a;
}
}
.rule {
#ns.mixin-1();
}
/* OUTPUTS:
.rule {
prop: one;
}
*/
This is a list of mixin scope features that may be removed in future releases.
#ns {
.mixin-1() {
prop: @a;
}
}
.rule {
@a: one;
#ns.mixin-1();
}
/* OUTPUTS:
.rule {
prop: one;
}
*/
This is counter-intuitive because:
Preferred approach: Pass in the variable you want to be visible to the mixin.
#ns {
.mixin-1(@a) {
prop: @a;
}
}
.rule {
#ns.mixin-1(@a: one);
}
Mixins will push their variables into the caller scope, but only if the variable is not locally defined.
#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;
}
*/
This is counter-intuitive because:
Also, with the introduction of Maps, you can retrieve variable values (and mixins) directly.
Preferred approach:
#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;
}
*/
Similarly to deprecated variable behavior, mixins are also pushed into the caller scope. However, unlike variables, mixins with the same name as the merged scope mixin are merged.
#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;
}
*/
Preferred approach: Call mixins directly.
#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;
}
*/
Credit: less/less.js/issues/1472
Here is a trick for defining variables and keeping them in some private scope, preventing them from leaking to the global space.
& {
// 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
}
Here, @height
and @width
are only defined for the scope created by & { ... }
You can also nest an scope inside a rule:
.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"'s around selectors
Released v1.5.0
Like Mixin Guards, guards can also be applied to css selectors, which is syntactic sugar for declaring the mixin and then calling it immediately.
For instance, before 1.5.0 you would have had to do this:
.my-optional-style() when (@my-option = true) {
button {
color: white;
}
}
.my-optional-style();
Now, you can apply the guard directly to a style.
button when (@my-option = true) {
color: white;
}
You can also achieve an if
type statement by combining this with the &
feature, allowing you to group multiple guards.
& when (@my-option = true) {
button {
color: white;
}
a {
color: blue;
}
}
Note that you can also achieve a similar pattern by using the actual if()
function and a variable call. As in:
@dr: if(@my-option = true, {
button {
color: white;
}
a {
color: blue;
}
});
@dr();
Released v2.5.0
Import JavaScript plugins to add Less.js functions and features
Using a @plugin
at-rule is similar to using an @import
for your .less
files.
@plugin "my-plugin"; // automatically appends .js if no extension
Since Less plugins are evaluated within the Less scope, the plugin definition can be quite simple.
registerPlugin({
install: function(less, pluginManager, functions) {
functions.add('pi', function() {
return Math.PI;
});
}
})
or you can use module.exports
(shimmed to work in browser as well as Node.js).
module.exports = {
install: function(less, pluginManager, functions) {
functions.add('pi', function() {
return Math.PI;
});
}
};
Note that other Node.js CommonJS conventions, like require()
are not available in the browser. Keep this in mind when writing cross-platform plugins.
What can you do with a plugin? A lot, but let's start with the basics. We'll focus first on what you might put inside the install
function. Let's say you write this:
// my-plugin.js
install: function(less, pluginManager, functions) {
functions.add('pi', function() {
return Math.PI;
});
}
// etc
Congratulations! You've written a Less plugin!
If you were to use this in your stylesheet:
@plugin "my-plugin";
.show-me-pi {
value: pi();
}
You would get:
.show-me-pi {
value: 3.141592653589793;
}
However, you would need to return a proper Less node if you wanted to, say, multiply that against other values or do other Less operations. Otherwise the output in your stylesheet is plain text (which may be fine for your purposes).
Meaning, this is more correct:
functions.add('pi', function() {
return new tree.Dimension(Math.PI);
});
Note: A dimension is a number with or without a unit, like "10px", which would be less.Dimension(10, "px")
. For a list of units, see the Less API.
Now you can use your function in operations.
@plugin "my-plugin";
.show-me-pi {
value: pi() * 2;
}
You may have noticed that there are available globals for your plugin file, namely a function registry (functions
object), and the less
object. These are there for convenience.
Functions added by a @plugin
at-rule adheres to Less scoping rules. This is great for Less library authors that want to add functionality without introducing naming conflicts.
For instance, say you have 2 plugins from two third-party libraries that both have a function named "foo".
// lib1.js
// ...
functions.add('foo', function() {
return "foo";
});
// ...
// lib2.js
// ...
functions.add('foo', function() {
return "bar";
});
// ...
That's ok! You can choose which library's function creates which output.
.el-1 {
@plugin "lib1";
value: foo();
}
.el-2 {
@plugin "lib2";
value: foo();
}
This will produce:
.el-1 {
value: foo;
}
.el-2 {
value: bar;
}
For plugin authors sharing their plugins, that means you can also effectively make private functions by placing them in a particular scope. As in, this will cause an error:
.el {
@plugin "lib1";
}
@value: foo();
As of Less 3.0, functions can return any kind of Node type, and can be called at any level.
Meaning, this would throw an error in 2.x, as functions had to be part of the value of a property or variable assignment:
.block {
color: blue;
my-function-rules();
}
In 3.x, that's no longer the case, and functions can return At-Rules, Rulesets, any other Less node, strings, and numbers (the latter two are converted to Anonymous nodes).
There are times when you may want to call a function, but you don't want anything output (such as storing a value for later use). In that case, you just need to return false
from the function.
var collection = [];
functions.add('store', function(val) {
collection.push(val); // imma store this for later
return false;
});
@plugin "collections";
@var: 32;
store(@var);
Later you could do something like:
functions.add('retrieve', function(val) {
return new tree.Value(collection);
});
.get-my-values {
@plugin "collections";
values: retrieve();
}
A Less.js plugin should export an object that has one or more of these properties.
{
/* 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() { },
}
The PluginManager instance for the install()
function provides methods for adding visitors, file managers, and post-processors.
Here are some example repos showing the different plugin types.
While a @plugin
call works well for most scenarios, there are times when you might want to load a plugin before parsing starts.
See: Pre-Loaded Plugins in the "Using Less.js" section for how to do that.