LESS setting variable inside mixin
Once a variable is declared within a namespace or a mixin, it kind of becomes scoped or accessible only within that namespace only and it's value cannot be access outside of the scope.
Reference from Less Website: Note that variables declared within a namespace will be scoped to that namespace only and will not be available outside of the scope via the same syntax that you would use to reference a mixin (
#Namespace > .mixin-name
). So, for example, you can't do the following: (#Namespace > @this-will-not-work
).
Solution 1:
One of the options for this particular case would be to use an unnamed namespace (&
) and call the mixin within it like below:
@light: #f5f5f5;
@nav-color: #0ff;
@headerbar: #333;
@dark: #222;
@light: #f5f5f5;
.theme() when (lightness(@headerbar) > 50%) {
@nav-color: @dark;
}
.theme() when (lightness(@headerbar) <= 50%) {
@nav-color: @light;
}
&{
.theme();
div#sample1{
color: @nav-color;
}
div#sample2{
background-color: @nav-color;
}
}
All the following options are courtesy of [seven-phases-max]'s comment and are added to the answer for completeness sake.
Solution 2: Removing the default value for the @nav-color
variable seems to make the code in question work as-is. This should not create any issues because either one of the .theme()
mixin's guard conditions would always be matched and hence the variable would always get a value assigned.
@light: #f5f5f5;
@headerbar: #333;
@dark: #222;
@light: #f5f5f5;
.theme() when (lightness(@headerbar) > 50%) {
@nav-color: @dark;
}
.theme() when (lightness(@headerbar) <= 50%) {
@nav-color: @light;
}
.theme();
div#sample1{
color: @nav-color;
}
Solution 3:
A completely different approach to solve this problem would be to use the built-in contrast()
function mentioned by seven-phases-max in this answer to totally avoid the mixin and set the variable values directly based on the lightness or darkness of another variable.
Additional Information:
To further illustrate the point, the below would work fine (though it is not exactly what you are after) and would output the correct color because the value for the @nav-color
is set within its scope.
.theme() when (lightness(@headerbar) > 50%) {
@nav-color: @dark;
div#sample3{
border-color: @nav-color;
}
}
.theme() when (lightness(@headerbar) <= 50%) {
@nav-color: @light;
div#sample3{
border-color: @nav-color;
}
}
Can you set a variable inside a mixin in LESS CSS?
Yes, it can be done. There was one bug that needed to be worked around.
Example LESS Code
//Set up guarded mixins
.setHeight(@h) when (ispercentage(@h)) {
@height: 1024px;
}
.setHeight(@h) when not (ispercentage(@h)) {
@height: @h;
}
//set up main height
@mainHeight: 50%;
body { height: @mainHeight;}
//call it by itself to make global
//.setHeight(@mainHeight); <-this failed (appears to be a bug)
.setHeight(50%); // <-this worked
.subsection { height: @height; /* just to show it is setting it */}
//use it for other globals
@textAreaHeight: 0.5 * @height;
@buttonHeight: 0.2 * @height;
textarea { height: @textAreaHeight}
button { height: @buttonHeight}
//override it locally
body.fixedHeight {
.setHeight(300px);
@textAreaHeight: 0.333 * @height;
height: @height;
textarea { height: @textAreaHeight}
}
Example CSS Output
body {
height: 50%;
}
.subsection {
height: 1024px; /* just to show it is setting it */
}
textarea {
height: 512px;
}
button {
height: 204.8px;
}
body.fixedHeight {
height: 300px;
}
body.fixedHeight textarea {
height: 99.9px;
}
use variables on mixin or extend in Less.js
You are trying to call a mixin using selector interpolation, which is not possible.
As for extend, Less documentation states it clearly:
Extend is NOT able to match selectors with variables. If selector contains variable, extend will ignore it.
LESS CSS - Setting variable within mixin
UPDATE I just reread your comment and understand the problem better. This should work:
.secColor (@bgc, @prop) when (lightness(@bgc) >= 50%) and (@prop = color){
color: black;
}
.secColor (@bgc, @prop) when (lightness(@bgc) >= 50%) and (@prop = background){
background-color: black;
}
.secColor (@bgc, @prop) when (lightness(@bgc) >= 50%) and (@prop = border){
border-color: black;
}
.secColor (@bgc, @prop) when (lightness(@bgc) >= 50%) and (@prop = all){
color: black;
background-color: black;
border-color: black;
}
.secColor (@bgc, @prop) when (lightness(@bgc) < 50%) and (@prop = color){
color: white
}
.secColor (@bgc, @prop) when (lightness(@bgc) < 50%) and (@prop = background){
background-color: white;
}
.secColor (@bgc, @prop) when (lightness(@bgc) < 50%) and (@prop = border){
border-color: white;
}
.secColor (@bgc, @prop) when (lightness(@bgc) < 50%) and (@prop = all){
color: white;
background-color: white;
border-color: white;
}
Then use the mixin:
.class1 {
.secColor (#fff, color) //should only set the color property for class1
}
.class2 {
.secColor (#000, all) //should set all three properties for class2
}
ADDED MORE COMPACT VERSION
.propSwitch (@prop, @clr) when (@prop = color) {
color: @clr;
}
.propSwitch (@prop, @clr) when (@prop = background) {
background-color: @clr;
}
.propSwitch (@prop, @clr) when (@prop = border) {
border-color: @clr;
}
.propSwitch (@prop, @clr) when (@prop = all) {
color: @clr;
background-color: @clr;
border-color: @clr;
}
.secColor (@bgc, @prop) when (lightness(@bgc) >= 50%) {
.propSwitch (@prop, #000);
}
.secColor (@bgc, @prop) when (lightness(@bgc) < 50%) {
.propSwitch (@prop, #fff);
}
Less mixin and variables
No, there is no need to specify the default value for the first parameter while calling the function. Instead you can just use named parameters feature to explicitly let the compiler know that the value you are passing in the mixin call is for the 2nd parameter.
.sample{
.iconFont(@font-size:14px);
}
The above Less code when compiled would produce the below output. (Note: I had set the @green
as #00ff00
.)
.sample {
color: #00ff00;
font-size: 14px;
}
While using the named parameter feature, even the order in which the parameters are passed does not matter. For example, the same mixin can be called as follows:
.sample2{
.iconFont(@font-size:24px, @color: #070707);
}
And it would produce the below as output.
.sample2 {
color: #070707;
font-size: 24px;
}
How to get an inner variable inside a mixin in less?
Your mistake is in assuming that & when
construction is sort of if
-like thing of C-like languages. But it's not, & {}
is just a plain CSS ruleset (just like div {}
for example) with &
as its selector, and as a plain ruleset it does not expose any internal variables to outer scopes.
Only mixins expose their internals to the outer scope when invoked, so one of the methods to achieve what you need is:
@list: #000, #fff;
div {
.custom-colors(@list, 0%, 20%, true);
color: @gradient;
}
.custom-colors(@value, @light, @dark, @lightdark) when (@lightdark = true) {
@gradient: lighten(extract(@value, 1), @light),
darken(extract(@value, 2), @dark);
}
.custom-colors(@value, @light, @dark, @lightdark) when (@lightdark = false) {
@gradient: darken(extract(@value, 1), @dark),
lighten(extract(@value, 2), @light);
}
That can be further simplified to:
@list: #000, #fff;
div {
.custom-colors(@list, 0%, 20%, true);
color: @gradient;
}
.custom-colors(@value, @light, @dark, true) {
@gradient: lighten(extract(@value, 1), @light),
darken(extract(@value, 2), @dark);
}
.custom-colors(@value, @light, @dark, false) {
@gradient: darken(extract(@value, 1), @dark),
lighten(extract(@value, 2), @light);
}
Also note that darken(somecolor, somevalue)
is equal to lighten(somecolor, -somevalue)
and vice-versa, so the whole thing can be further optimized depending on the actual snippet.
Dynamically define a variable in LESS CSS
This Cannot Be Done
What you desire to do is not currently possible in LESS. I can think of two possible "workarounds" if you know ahead of time what variable names you want to allow to be used (in other words, not fully dynamic). Then something like one of the following could be done:
Idea #1 (Variable Variables)
.define(@var) {
@fooBar: 0;
@fooTwo: 2;
@fooYep: 4;
@fooSet: 'foo@{var}';
}
.define(Two);
.test {
.define(Bar);
prop: @@fooSet;
}
.test2 {
prop: @@fooSet;
}
Idea #2 (Parametric Mixins)
LESS
.define(@var) {
.foo() when (@var = Bar) {
@fooBar: 0;
}
.foo() when (@var = Two) {
@fooTwo: 2;
}
.foo() when (@var = Yep) {
@fooYep: 4;
}
.foo();
}
.define(Two);
.test {
.define(Bar);
prop: @fooBar;
}
.test2 {
prop: @fooTwo;
}
CSS Output (for both ideas)
.test {
prop: 0;
}
.test2 {
prop: 2;
}
Conclusion
But I'm not sure how useful either would really be, nor do I know if it could have any real application in your actual use case (since you mention the above is not the real use case). If you want a fully dynamic variable in LESS, then it cannot be done through LESS itself.
Less CSS: Can I call a mixin as an argument when calling another mixin?
As discussed in comments, Less mixins are not functions and the mixin calls cannot return any value. Because of this, one mixin (or its output value) cannot be passed as an argument to another mixin.
Having said that, we can still set a variable within a mixin, call the mixin within each selector block where it is required and make use of the variable defined within it. The mixin call effectively exposes the variable defined within it to the parent scope.
Below is a sample snippet which would call the contrast mixin and assign the calculated value as the text color and border color of the element.
// color variables for user's color
@userColor: #13acae;
@darkUser: hsl(hue(@userColor), saturation(@userColor), lightness(tint(@userColor, 30%)));
@lightUser: hsl(hue(@userColor), saturation(@userColor), lightness(shade(@userColor, 30%)));
// color mixin to alter user's color using Less 'darken' and 'contrast' functions
.contrastColorDark(@percent) {
@color: darken(contrast(@userColor, @darkUser, @lightUser), @percent);
//color: darken(contrast(@userColor, @darkUser, @lightUser), @percent);
}
// border mixin
.border(@width, @color) {
border: @width solid @color;
}
// CSS rule using both mixins
.thing {
.contrastColorDark(10%);
color: @color;
.border(1px, @color);
}
.thing2 {
.contrastColorDark(50%);
color: @color;
.border(1px, @color);
}
Related Topics
Absolutely Positioned Flexbox Doesn't Expand to Fit Contents
Currentcolor Inheritance for Svg Url
How to Align One Element to The Right and One Element to The Left Inside a Containing Element
Making an Unordered List Span 100% The Width of a Div
Ionic-Item Color and Href Not Working
How to Align Panelgrid to Center? Jsf-Primefaces
Multiple HTML Div's Using Same CSS Style
Django Staticfiles at Url Root
Matrix Scale Transition Not Working
How to Add Focus to a Table Row <Tr>
Strange Border-Width Behavior in Chrome - Floating Point Border-Width
How to Always Center a Flexible Square in Viewport with Pure CSS
Error While Configuring CSS Modules with Webpack
Editing Angular Material's Table Cell Padding
How to Style a Text Input to Fill The Width of It's Parent