Built-in functions not working with evaluated strings, why? - css

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

Concatenate variable with string to form another variable

#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.

Escapes, Fades and Variable Colors [LESS] [duplicate]

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.

Using a Sass variable mapped to an hsl value doesn't work when trying to use it with hsla

I have a Sass variable which mapped to an hsl value. When I try to use it with hsla to add a little transparency, doesn't work. I'm doing this:
$white:hsl(100, 100%, 100%);
.thing{
color:hsla($white,.9);
}
Using gulp-sass to build my CSS, I get this error: "required parameter $lightness is missing in call to function hsla on line {line number} in {file's path}"
If I replace the hsla with rgba it works fine and, yes, I can do that, but I'd like to keep all my colors in hsl. Is there a workaround or is this a Sass issue?
It's not an issue with SASS, the functionality simply doesn't exist. If you look at the documentation, there are two versions of rgba(), one that accepts all of the parameters separately and one that accepts a Color object.
rgba($red, $green, $blue, $alpha)
rgba($color, $alpha)
If you look at the documentation for hsla(), it only accepts the values separately.
hsla($hue, $saturation, $lightness, $alpha)
To achieve your goal, you could do this:
$white:hsl(100, 100%, 100%);
.thing{
color: hsla(hue($white), saturation($white), lightness($white), .9);
}
Or... if you want to pass the Color object, you can create your own function since you can't overload functions; e.g. hslac($color, $alpha)
#function hslac($color, $alpha) {
#if(type-of($color) == "color") {
#return hsla(hue($color), saturation($color), lightness($color), $alpha);
}
#else {
#error "You didn't pass a color object";
}
}

Passing a variable name as a variable into the lighten function doesn't work

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

How to use variable variables in LESS with colors stored in named variables in loops and mixins?

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) }

Resources