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:
- It is not typical in most other languages.
- It's not immediately obvious when looking at the definition what output will be produced by the mixin.
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:
- A variable higher in the caller scope can be overridden.
- It's also not a typical language behavior.
- It differs from the behavior of detached rulesets.
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
}
}
}