It seems that evaluated color strings are not working with some built-in LESS functions.
I have tried using e() and ~"" and any combination of both.
I might find a workaround for my particular case, I’m just asking if this is this expected behaviour, or if there is a fault in my reasoning? Any insight appreciated.
For example here, the color is created from an evaluated string; note the 'missing' # in the hex value that gets added later :
.broken-mixin(#hexcode: '9719e1') {
#color: e("##{hexcode}");
// this works as expected
background-color: #color;
// this does work too
.very-simple-mixin(#color);
// Undefined_methodError: error evaluating function `fade`:
// Object #<Object> has no method 'toHSL'
background-color: fade(#color,30%);
// SyntaxError: error evaluating function `red`:
// Cannot read property '0' of undefined
background-color: rgba(red(#color), green(#color), blue(#color), 0.5);
}
Otherwise built-in functions work normally work with variables in mixins, for example :
.mixin-works(#myColor: #00ff00) {
// works just fine
background-color: fade(#myColor,30%);
// or this, works too
background-color: rgba(red(#myColor), green(#myColor), blue(#myColor), 0.5);
}
What am I missing ?
Quoting the LESS website's Function Reference:
fade
Set the absolute transparency of a color. Can be applied to colors whether they already have an opacity value or not.
Parameters:
color: A color object.
amount: A percentage 0-100%.
The fade function requires a color object as input to it and hence passing an evaluated string as a parameter to the function doesn't work.
It can be solved by using the built-in color function which converts a string into an equivalent color object like below:
background-color: fade(color("#{color}"),30%);
The other built-in functions also are not working for the same reason (that is, they expect a color object as an input).
red:
Extracts the red channel of a color object.
Parameters: color - a color object.
Related
#color-purple: "#ffffff"
#colors: purple, light-purple, green, light-green, red, light-red, grey, light-grey, lightest-grey;
.ColorsMixin(#i:0) when(#i =< length(#colors)){ //loop over icons array
#color: extract(#colors, #i); //extract the icon at current index #i
.color--#{color}{
background: #{color-#{color}};
&:before{
content: "#{color}";
}
&:after{
content: "\#{color-#{color}}";
}
}
.ColorsMixin(#i + 1);
}
.ColorsMixin();
So, I can get it to do what I want to do in the
content: "\#{color-#{color}}";
part. This will output
content: "#ffffff";
However, when I try to output the #color-purple variable as the background, LESS throws an error. It only seems to work if I wrap it in quotation marks, but the background property wants the hex code without the quotes around it.
What's the trick here?
background: #{color-#{color}};
is not valid Less syntax, the proper one would be:
background: ~'#{color-#{color}}';
Note however, the very idea of indirectly refering to a variable values via escaping is a durty kludge (quite wide-spread but still very dirty).
It works when you assign such value directly to CSS property, but it will fail for anything else, simply because such value is not a color anymore but an unquoted string with an unknown content...
E.g. the following code will fail:
#color-dark-purple: #321;
div {
#color: 'color-dark-purple';
background: fade(~'#{color}', 50%); // error, not a color value
}
The proper Less method of getting a variable value via its name is "variable reference", e.g.:
#color-dark-purple: #321;
div {
#color: 'color-dark-purple';
background: fade(##color, 50%); // OK, proper color value
}
Additionally, take a time to consider if the whole approach of having all these colors as distinct variables and then having a separate list of these variables names is really what you need. Normally a single list having both color names and values is not such awfully bloating and much more maintainable.
I'm trying to call a mixin as an argument in another mixin but I get a syntax error. There's no variables in the offending mixin call, just arguments.
I'm not sure if this is possible. The answers I've seen on here seem to be either hacks or to be dealing with variables and strings as arguments.
Less CSS
// 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); }
// border mixin
.border(#width, #color) { border: #width solid #color; }
// CSS rule using both mixins
.thing {
.border(1px, .contrastColorDark(10%));
}
Error (at the dot before .contrastColorDark(10%) )
SyntaxError: expected ')' got '.'
What I am trying to achieve: I am trying to get the box border color to match certain elements inside it that are using the contrast 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);
}
This works:
background-color: ~"#{#{space-name}-color-4}";
This does not:
background-color:lighten(~"#{#{space-name}-color-4}",5%);
Error:
SyntaxError: error evaluating function lighten: Object # has no method 'toHSL' in ...
Checked questions on SO already and this one seems to be similar:
Define variable name with variable in LESS operation
Unfortunately this did not work for me, when I used:
#color4:~"#{#{space-name}-color-4}";
border: 1px solid #color4; // this works
background-color:lighten(#ffffff,5%); // this works
background-color:lighten(#color4,5%); // this doesn't
background-color:lighten(##color4,5%); // this doesn't - throws 'SyntaxError: variable ##{my-color-4} is undefined in..' although it is defined as #my-color-4 previously. Somehow double # seems to fail
background-color:lighten(color(#color4),5%); // this doesn't
Seems to be something with https://github.com/less/less.js/issues/1458 but I am not able to make a workaround as mentioned.
What am I doing wrong?
Set up mixins something like this:
#space-name: space;
#space-color-4: #123456;
#color4:~'#{space-name}-color-4';
Then in your class they can be used as follows:
.class {
border: 5px solid ##color4; // this works
background:lighten(##color4,25%); // this also works
}
Codepen demo
It seems that evaluated color strings are not working with some built-in LESS functions.
I have tried using e() and ~"" and any combination of both.
I might find a workaround for my particular case, I’m just asking if this is this expected behaviour, or if there is a fault in my reasoning? Any insight appreciated.
For example here, the color is created from an evaluated string; note the 'missing' # in the hex value that gets added later :
.broken-mixin(#hexcode: '9719e1') {
#color: e("##{hexcode}");
// this works as expected
background-color: #color;
// this does work too
.very-simple-mixin(#color);
// Undefined_methodError: error evaluating function `fade`:
// Object #<Object> has no method 'toHSL'
background-color: fade(#color,30%);
// SyntaxError: error evaluating function `red`:
// Cannot read property '0' of undefined
background-color: rgba(red(#color), green(#color), blue(#color), 0.5);
}
Otherwise built-in functions work normally work with variables in mixins, for example :
.mixin-works(#myColor: #00ff00) {
// works just fine
background-color: fade(#myColor,30%);
// or this, works too
background-color: rgba(red(#myColor), green(#myColor), blue(#myColor), 0.5);
}
What am I missing ?
Quoting the LESS website's Function Reference:
fade
Set the absolute transparency of a color. Can be applied to colors whether they already have an opacity value or not.
Parameters:
color: A color object.
amount: A percentage 0-100%.
The fade function requires a color object as input to it and hence passing an evaluated string as a parameter to the function doesn't work.
It can be solved by using the built-in color function which converts a string into an equivalent color object like below:
background-color: fade(color("#{color}"),30%);
The other built-in functions also are not working for the same reason (that is, they expect a color object as an input).
red:
Extracts the red channel of a color object.
Parameters: color - a color object.
I have a LESS loop in which I determine color values to use in CSS rules.
I get them through some quite complex vars evaluation, which forces me to use strings (If I remove the " I get a parse error).
So what I get is a variable containing a color value in form of string.
#color: "#{col_#{animal}}"
// this is in a loop, and #animal contains the name of a var ('dog', 'cat', ...)
// #col_dog, #col_cat contain a color
// #col_dog: #F9E2A0
// #col_cat: #094DD0
so if I try to assign this #color variable to a rule
.border { border-color: #color }
in CSS I get
.border {border-color: "#F9E2A0"}
Which obviously is ignored.
Is there a way to get rid of the "string" form, or a way to do the vars evaluation I need without using strings?
Thanks!
It's easy just use ##
I've been struggling with this myself for some time now. The solution is simple. Just use ## instead of # for the color. The color will then get parsed properly, and become an color object. For this to work I store the variable name 'color_cat' in a variable called #color first. The I use the variable variables technique ## to resolve the variable.
In your case this code works:
#color_dog: red;
#color_cat: yellow;
.animal-border(#animal){
#color: "color_#{animal}";
.#{animal}.border{
border-color: ##color;
}
}
.animal-border(dog);
.animal-border(cat);
Results:
.dog.border {
border-color: #ff0000;
}
.cat.border {
border-color: #ffff00;
}
Some errors associated with this problem. This one occurs when using the darken or lighten methods:
error evaluating function darken: Object # has no method 'toHSL'
Or this occurs when trying to supply the string value "#FF0000" to the color method:
error evaluating function color: argument must be a color keyword or 3/6 digit hex e.g. #FFF
Some related posts on SO:
Define variable name with variable in LESS operation
less undefined method error
Lighten color from parent in Less
Defining Variable Variables using LESS CSS
According to the docs http://lesscss.org/functions/#misc-functions-color
Parses a color, so a string representing a color becomes a color.
This should be doing what you want:
.border { border-color: color(#color) }