-
Notifications
You must be signed in to change notification settings - Fork 47
Quirks
Mixins parameters are either semicolon or comma separated. It is recommended to use semicolon. Comma as mixins parameters separator is kept only for backward compatibility.
The symbol comma has double meaning: it can be interpreted either as a mixin parameters separator or CSS list separator. Using comma as mixin separator makes it impossible to create comma separated lists as an argument. On the other hand, if the compiler sees at least one semicolon inside a mixin call or declaration, the compiler assumes that arguments are separated by semicolons and all commas belong to css lists:
- two arguments and each contains comma separated list:
.name(1, 2, 3; something, else)
, - three arguments and each contains one number:
.name(1, 2, 3)
, - use dummy semicolon to create mixin call with one argument containing comma separated css list:
.name(1, 2, 3;)
, - comma separated default value:
.name(@param1: red, blue;)
.
Css uses two types of expressions separators: a space (' ') and a comma (','). Comma is not allowed to be inside a parentheses e.g., this something: (12 (13 + 10,-23));
throws compile exception.
However, space is allowed inside the parentheses and is treated the same way as a top level separator. If they contain the empty separator, the output will contain separator too. Parentheses influence the order of arithmetic operations, but the user should be careful about spaces inside them.
Related less.js discussion: https://github.com/cloudhead/less.js/issues/952
Sample input:
parentheses {
something: (12 (13 + 10 -23));
}
Sample output:
parentheses {
something: 12 23 -23;
}
Some more cases:
something: (12 + (13 + 10 -23)); //Compiles into 12
something: (12 * (13 + 5 -23)); //Compiles into NaN
something: (12 (13 + 5 -23) + 5); //Compile error: Object doesn't support this property or method
Less.js adds four arithmetic operations into CSS: addition (+), subtaction (-), multiplication (*) and division (/). You can use spaces inside those expressions e.g., following two expressions are equivalent:
2 + 3 * 4
-
2+3*4
.
This feature interacts with standard CSS in an unfortunate way. CSS declarations use spaces as a separator and they allow also negative values. Correct CSS:
something {
margin: -1 -2 -3 -4;
}
Combination of spaces as separators, negative numbers and expressions causes spaces to be significant. Two expressions with the same numbers and operators may mean two different things. Declarations in following example differ only in spaces, but their meaning is different:
one {
margin: 1 -2; //compiles into "margin: 1 -2;"
}
two {
margin: 1 - 2; //compiles into "margin: -3;"
}
Space is not allowed between function name and parenthesis. This is NOT considered a function: width: increment (15);
and is compiled into width: increment (15);
. The space must be removed e.g., width: increment(15);
compiles to width: 16;
Note: if they less.js would allow a space after the function name, the grammar would become ambiguous. The only way to differentiate between an identifier followed by expression and an function would be to keep a list of allowed functions and restrict those identifiers on anything except them.
The last variable declaration within the same scope wins - less was defined this way so it is consistent with how css behaves. See https://github.com/cloudhead/less.js/issues/297 and https://github.com/cloudhead/less.js/issues/905 issues in less.js project for related discussions.
Sample input:
@var: @a;
@a: 100%;
.lazy-eval {
width: @var;
}
@a: 50%;
.lazy-eval two {
width: @var;
}
Compiles into:
.lazy-eval {
width: 50%;
}
.lazy-eval two {
width: 50%;
}
Nth expression is not allowed as a variable value. E.g. this is NOT syntactically correct:
@nth:3n+2;
.values:nth-child(@nth) {
declaration:value;
}
Triple variables indirection does not work. E.g. this is NOT syntactically correct:
.variable-names {
@var: 'hello';
@almostthere: 'var';
@name: 'almostthere';
name1: @@almostthere;
name: @@@name;
}
The last line throws the exception. On the other hand, this is OK:
.variable-names {
@var: 'hello';
@almostthere: 'var';
@name: 'almostthere';
name1: @@almostthere;
}
and compiles into:
.variable-names {
name1: 'hello';
}
Less.js reorders statements in @media. It prints declarations first and rulesets second. The last declaration does not have a new line, following ruleset starts at the same line.
Any mixin can use all variables and mixins accessible on place where it is called. Both definition and caller scopes are available to it. If both scopes contains the same variable or mixin, declaration scope value takes precedence.
Mixin can use variables and mixins declared in the caller scope:
.mixin() {
declaration: @callerVariable;
.callerMixin();
}
selector {
@callcallerVariableerScope: 10;
.callerMixin() {
variable: declaration;
}
.mixin();
}
is compiled into:
selector {
declaration: 10;
variable: declaration;
}
Declaration scope value takes precedence:
.mixin() {
@scope: 5;
declaration: @scope;
}
selector {
@scope: 10;
.mixin();
}
is compiled into:
selector {
declaration: 5;
}
Variables and mixins defined inside mixins act as return values. Since return values in 1.3.x have been made in potentially dangerous way, the exact behavior of this feature changed between 1.3.x and 1.4.x versions.
Variables defined in mixin are usable and rewrite variables defined in caller, so it looks like the whole mixins body is copied into the caller. The last variable declaration win rule is then used to solve variables, so mixin will rewrite callers variable value.
Mixin rewrites caller variable:
.defaultPadding() {
@size: 2;
@definedInMixin: 10;
}
.class {
@size: 3; //irrelevant declaration
margin: @size @definedInMixin;
.defaultPadding();
}
Compiles into:
.class {
margin: 2 10;
}
If it is a problem, last section contains workaround.
If you want to be sure that your mixin will not pollute its callers scope, define it in a separate namespace and declare all its variables and mixins in that namespace. No matter what language variant, mixin will be able to access them, but they will not be copied into mixins caller.
Sample input:
#encapsulation {
@size: 2;
.defaultPadding() {
padding: @size;
}
}
class {
@size: 3;
margin: @size;
#encapsulation > .defaultPadding();
}
compiles into:
class {
margin: 3;
padding: 2;
}
Variables defined in mixin acts as return values and are usable in caller, but they will not rewrite callers local variables.
Mixin does not rewrite callers local variables, but variables not present in the callers local scope are still copied there:
@nonLocal: original-value;
.defaultPadding() {
@size: in-mixin-value;
@definedInMixin: in-mixin-value;
@nonLocal: in-mixin-value;
}
.class {
@size: 3;
margin: @size @definedInMixin @nonLocal;
.defaultPadding();
}
Compiles into:
.class {
margin: 3 in-mixin-value in-mixin-value;
}