Passing a variable list through .for loop using Less 2.6.1 is unable to compile - less

I have been using a Less mixin which makes looping in Less, less verbose and is really handy: https://github.com/seven-phases-max/less.curious/
I upgraded from Less 2.6.0 to Less 2.6.1 and now the Less will not compile.
Here's the Less code:
// ............................................................
// .for
.for(#i, #n) {.-each(#i)}
.for(#n) when (isnumber(#n)) {.for(1, #n)}
.for(#i, #n) when not (#i = #n) {
.for((#i + (#n - #i) / abs(#n - #i)), #n);
}
// ............................................................
// .for-each
.for(#array) when (default()) {.for-impl_(length(#array))}
.for-impl_(#i) when (#i > 1) {.for-impl_((#i - 1))}
.for-impl_(#i) when (#i > 0) {.-each(extract(#array, #i))}
#gray-base: #000;
#gray-darker: #222;
#gray-dark: #333;
#gray: #555;
#gray-light: #777;
#gray-lighter: #eee;
#gray-type:
gray,
gray-base,
gray-dark,
gray-darker,
gray-light,
gray-lighter;
.gray-type {
// Mixin to generate the gray font colours for p tags in the font stacks.
.for(#gray-type); .-each(#name) {
.#{name} {
color: ##name;
}
}
}
Here's the expected output:
.gray-type .gray {
color: #555;
}
.gray-type .gray-base {
color: #000;
}
.gray-type .gray-dark {
color: #333;
}
.gray-type .gray-darker {
color: #222;
}
.gray-type .gray-light {
color: #777;
}
.gray-type .gray-lighter {
color: #eee;
}
After upgrading to Less 2.6.1 I get the following error:
>> SyntaxError: variable #[object Object],[object Object] is undefined in less/style.less on line 41, column 3:
>> 40 // Mixin to generate the gray font colours for p tags in the font stacks.
>> 41 .for(#gray-type); .-each(#name) {
>> 42 .#{name} {
Warning: Error compiling less/style.less Use --force to continue.
I think the problem is related to the hyphens being stripped from the variables during compilation, therefore making the variables passed though the list invalid.
Any help would be greatly appreciated.

Related

how to use "sass:meta" in sass-loader on vue component

I have the next error on my component
SassError: Invalid CSS after "... $color in meta": expected expression (e.g. 1px, bold), was ".keywords($args) {"
on line 5 of src/assets/scss/components/_dMetricCircle.scss
from line 101 of src/components/UI/DMetricCircle.vue
#each $name, $color in meta.keywords($args) {
and this is my _dMetricCircle.scss
#use "sass:meta";
#mixin test-mixin($args...){
#each $name, $color in meta.keywords($args) {
.#{$name} {
background-color: map-get($color, "bgcolor");
border: 1px solid map-get($color, "border");
#if map-has-key($color, "pcolor") {
&.percent{
background: linear-gradient(var(--v), map-get($color, "pcolor") 50%, transparent 0) center/calc(var(--s) * 100%) border-box,
linear-gradient(var(--v), map-get($color, "bgcolor") 50%, transparent 0) center/calc(100% - var(--s) * 100%) border-box,
linear-gradient(to right, map-get($color, "pcolor") 50%, map-get($color, "bgcolor") 0) border-box;
}
}
}
}
}
and on my DMetricCircle component, I import and use the mixin like this
#include test-mixin(
$default: ("bgcolor": $gray-lighten-45, "border": $gray-lighten-30),
$success: ("bgcolor": $success-lighten-50, "border": $success-lighten-20, "pcolor": $gray-lighten-45),)
So, I don't know if sass-loader doesn't supper the use of meta or I'm doing a wrong use of sass compiler
"node-sass": "4.14.1","sass-loader": "10.0.2"

Less colors in rgba() even if alpha channel is 1

I need that Less compiler don't convert to HEX colors defined in rgba(), even if alpha channel is 1
Actually, the following Less code:
#color1: rgba(0,0,0,1);
#color2: rgba(0,0,0,0.1);
#color1_light: lighten(#color1,90%);
#color2_light: lighten(#color2,90%);
.a {
background:#color1;
color: #color1_light;
}
.b {
background:#color2;
color: #color2_light;
}
is processed to:
.a {
background: #000000;
color: #e6e6e6;
}
.b {
background: rgba(0, 0, 0, 0.1);
color: rgba(230, 230, 230, 0.1);
}
but I need to have (for many reasons related to further evaluations)
.a {
background: rgba(0, 0, 0, 1);
color: rgba(230, 230, 230, 1);
}
.b {
background: rgba(0, 0, 0, 0.1);
color: rgba(230, 230, 230, 0.1);
}
How to solve this?
Basically this is pretty easy, all you have to do is to escape your expression, below you will find two examples how to force less compiler to display rgba format even though alpha is 1:
#color1: rgba(0,0,0,1);
body {
color: ~"rgba("red(#color1), green(#color1), blue(#color1),~" 1)";
background: %(~"rgba(%s, %s, %s, 1)", red(#color1), green(#color1), blue(#color1));
}
Both examples will produce rgba(0, 0, 0, 1), it's up to you which one do you prefer. I bet you will find more info in the docs under string escape and string replace
//EDIT
yup, this is tricky, but still, you can extend this with a mixin so it won't look that bad in code hereafter.
#color1: rgba(0,0,0,1);
.rgba(#color) {
//#rgba: ~"rgba("red(#color), green(#color), blue(#color),~" 1)";
#rgba: %(~"rgba(%s, %s, %s, %s)", red(#color), green(#color), blue(#color), alpha(#color));
}
.torgba(#property, #color) {
#{property}: %(~"rgba(%s, %s, %s, %s)", red(#color1), green(#color1), blue(#color1), alpha(#color1));
}
body {
.rgba(#color1); // mixin returns #rgba variable that may be used later on, it's not color object however, but a string
color: #rgba;
background: #rgba;
.torgba(border-color, #color1); // do the same, but returns property with color in given format
}

less function to generate css class

I try to do any less function which will be called to create some classes.
Here is the way I tried :
.makeCssColor{#couleur) {
.coul_#{couleur} {
background-color: fade(~"#{couleur}, 'Fonce'", 15%);
&.open, &:hover {
background-color: ~"#{couleur}, 'Fonce'";
}
.btMod {
background : url('/img/btModEvt_#{couleur}.png') left top no-repeat transparent;
}
}
}
And I try to call it to create the classes :
.makeCssColor("bleu");
.makeCssColor("rouge");
But it generate an error. I don't find the good way to do it... And it bothers me to repeat all these code for each color (there is more than these line code and more thant two colors !).
Can anyone give me a little help ? :)
[edit]
ok, thanks to your help, this code does not generate an error, but there is a mistake in the CSS file :
#marronFonce = #9d5a1e;
.makeCssColor(#couleur) {
.coul_#{couleur} {
.top {
background-color: #couleur, 'Fonce';
}
.mod {
background : url('/img/btModEvt_#{couleur}.png') left top no-repeat transparent;
}
}
}
.makeCssColor(marron);
Generate this into the css file :
.coul_marron .top{background-color:marron,'Fonce'}
.coul_marron background : url('/img/btModEvt_marron.png') left top no-repeat transparent;
So the background color isn't good :
.coul_marron .top{background-color:#9d5a1e}
.coul_marron background : url('/img/btModEvt_marron.png') left top no-repeat transparent;
I need to evaluate #couleur, 'Fonce' : #marronFonce => #9d5a1e.
I tried #{#couleur, 'Fonce'} but it doesn't works...
Fade function takes a colour and a fade percentage, in your case you are passing 2 colours. Pass them one at a time. I also made some adjustments on #couleur since i some cases they don't need to be escaped
.makeCssColor{#couleur) {
.coul_#{couleur} {
background-color: fade(#couleur, 15%), fade(Fonce, 15%);
&.open, &:hover {
background-color: #couleur, 'Fonce';
}
.btMod {
background : url('/img/btModEvt_#couleur.png') left top no-repeat transparent;
}
}
}
when you call the mixin use the below, no need to use quotes
.makeCssColor(bleu);
UPDATE - just pass it in
.makeCssColor(#couleur, #name) {
.coul_#{name} {
.top {
background-color: #couleur;
}
.mod {
background : url('/img/btModEvt_#{name}.png') left top no-repeat transparent;
}
}
}
then when you call it
.makeCssColor(#marronFonce, marron);
OR
other option is you can make a loop, it's more complicated but you can try it. I am using an example I already have on my computer
first define a variable with the colour and names
#sample:
~"0070" '#ebebe7',
~"08x2" '#00247a',
~"01k0" '#92918e';
then loops thru it
.sample-loop ( #l ) when ( #l > 0 ) {
#item: extract( #sample #l );
#code: extract( #item, 1 );
#colour: color(extract( #item, 2 ));
.ext-#{code} {
background-color: #colour;
}
.sample-loop( #l - 1 );
}
and finally call the loop to generate your classes
.sample-loop( 3 );
depending on which version of less you have, the 3 can coded so it is dynamic. If you have older version of less then you have to hard code the length of the variable, or assign the length to a variable so you can use it anywhere

Smart margin mixin for LESS

I've created a .smartMargin() mixin for LESS CSS to be used in responsive design LESS.
I'm sharing it here for the benefit of others and also because I'm curious if there's any way to make it more efficient.
Idea
The mixin is designed to be called from within another mixin that represents a responsive design breakpoint. The idea is that you may want to override top, left, bottom, right margins individually by overriding one or more margins in each successive breakpoint.
In main mixin I just want one parameter for #videoMargin as opposed to #videoMarginLeft, #videoMarginRight etc so that's why I've called it 'smartMargin'.
Usage
In my main file I define a breakpoint like this, and then call this mixin several times:
.breakpoint(#width, #color, #labelsSize, #videoMargin)
{
video
{
.smartMargin(#videoMargin);
}
}
.breakPoint(10em, red, 3em, 1em auto 1em auto);
.breakPoint(10em, green, 3em, 2em unset unset unset);
.breakPoint(20em, blue, 3em, unset 3em unset unset);
Output css
So for a given value of #videoMargin here's the output css generated
generated css
-------------
.smartMargin(3em); margin: 3em;
.smartMargin(3em 1em 2em 4em); margin: 3em 1em 2em 4em;
.smartMargin(3em unset unset unset); margin-top: 3em;
.smartMargin(3em unset unset 4em); margin-top: 3em;
margin-right: 3em;
Implementation
The mixin is as follows. It works well but it just seems a little clumsy in places and you need to provide either 4 or 1 parameters. If anybody can optimize this I'd be very interested to see.
.smartMargin(#margin) when (length(#margin) = 4)
{
._smartMargin() when (extract(#margin, 4) = unset), (extract(#margin, 3) = unset), (extract(#margin, 2) = unset), (extract(#margin, 1) = unset)
{
.marginComponent(#name, #value)
{
& when not (#value = unset)
{
#{name}: #value;
}
}
.marginComponent(margin-top, extract(#margin, 1));
.marginComponent(margin-right, extract(#margin, 2));
.marginComponent(margin-bottom, extract(#margin, 3));
.marginComponent(margin-left, extract(#margin, 4));
}
._smartMargin() when (default())
{
margin: #margin;
}
._smartMargin();
}
.smartMargin(#margin) when (default())
{
& when not (#margin = ~'') and not (#margin = unset)
{
margin: #margin;
}
}
You could possibly rewrite it to something like:
.smartMargin(#margin) when (isem(#margin)),(isem(extract(#margin,1))) and (isem(extract(#margin,2))) and (isem(extract(#margin,3))) and (isem(extract(#margin,4))) {
margin: #margin;
}
.smartMargin(#margin) when (default()) and (4 = length(#margin)) {
#positions: top, right, bottom, left;
.setmargin(#position,#margin) when (isem(#margin)){
margin-#{position}: #margin;
}
.setmargins(#i:1) when (#i <= 4){
.setmargin(extract(#positions,#i);extract(#margin,#i));
.setmargins((#i + 1));
}
.setmargins();
}
But in the first place i don't think there is something wrong with your code. Personally is should consider the use of unset. I think you should use the initial keyword or even 0 in stead of unset. This enables you to do the following:
.smartMargin(#margin){
margin: #margin;
}
.one{
.smartMargin(3em);
}
.two{
.smartMargin(3em 1em 2em 4em);
}
.three{
.smartMargin(3em 0 0 0);
}
.four{
.smartMargin(3em 0 0 4em);
}
Or consider to use Passing Rulesets to Mixins, than you can use something like that shown below:
.breakPoint(#width, #color, #labelsSize, #videoMargin)
{
video
{
#videoMargin();
}
}
.breakPoint(10em, red, 3em, {margin: 1em auto 1em auto;});
.breakPoint(10em, red, 3em, {margin: 1em auto;});
.breakPoint(10em, green, 3em, {margin: 2em 0 0 0;});
.breakPoint(10em, green, 3em, {margin: 2em 0 0;});
.breakPoint(10em, green, 3em, {margin-top: 2em;});
.breakPoint(20em, blue, 3em, {margin: 0 3em 0 0;});
.breakPoint(20em, blue, 3em, {margin-right: 3em;});

Resolving dynamic variables in LESS

I am trying to generate a number of classes in a loop based on a number of pre-defined variable snippets.
I have a variables.less document that I am importing at the top of this less file containing my color variables. I then want to generate matching classes for these, but I am unable to get less to compile the variable.
My code:
.loop-class(~"primary", ~"success", ~"info", ~"warning", ~"danger";);
.loop-class(#list, #index: 1) when (isstring(extract(#list, #index))) {
#status: extract(#list, #index);
.button-#{status} {
color: ~'#button-#{status}';
}
.loop-class(#list, (#index + 1));
}
Which compiles to:
.button-primary {
color: #button-primary;
}
.button-success {
color: #button-success;
}
etc etc
As you can see, I get the variable name to concatenate correctly, but I can not get it to resolve, so I'm guessing that LESS has already done it's variable compilation before getting to this function?
I have already tried moving the variables into this document, as well as wrapping the variables in a mixin and adding that inside the .loop-class, but neither of these seemed to help.
I also tried something like:
#status: extract(#list, #index);
#compileClass: ~'#button-#{status}';
.button-#{status} {
color: #compileClass;
}
where I am saving the variable in a another one and then referencing that, but it yields the same result.
I looked at less css calling dynamic variables from a loop and tried implementing that as follows:
.loop-class(~"primary", ~"success", ~"info", ~"warning", ~"danger";);
.define(#var) {
#fallback: ~'#button-#{var}';
}
.loop-class(#list, #index: 1) when (isstring(extract(#list, #index))) {
#status: extract(#list, #index);
.button-#{status} {
.define(#status);
color: ##fallback;
}
.loop-class(#list, (#index + 1));
}
But that gave me the error that ##button-danger (last in the index) is undefined, so it still can't resolve the variable.
Is it obvious to you guys what I'm doing wrong?
Thanks for your help!
Missing Brackets
You are missing a set of needed brackets to resolve the variable:
LESS
//imported from another file
#button-primary: cyan;
#button-success: green;
#button-info: orange;
#button-warning: yellow;
#button-danger: red;
//in your mixin file
.loop-class(~"primary", ~"success", ~"info", ~"warning", ~"danger";);
.loop-class(#list, #index: 1) when (isstring(extract(#list, #index))) {
#status: extract(#list, #index);
.button-#{status} {
color: ~'#{button-#{status}}'; /* two more brackets needed */
| |
here here
}
.loop-class(#list, (#index + 1));
}
CSS Output
.button-primary {
color: #00ffff;
}
.button-success {
color: #008000;
}
.button-info {
color: #ffa500;
}
.button-warning {
color: #ffff00;
}
.button-danger {
color: #ff0000;
}
Cleaner More Friendly Code
Also, as a matter of less cluttered and more user friendly code, you can remove your multiple string interpolations needed for the mixing call by changing isstring to iskeyword in your mixin:
.loop-class(primary, success, info, warning, danger;); /* cleaner code on call */
.loop-class(#list, #index: 1) when (iskeyword(extract(#list, #index))) {
#status: extract(#list, #index);
.button-#{status} {
color: ~'#{button-#{status}}';
}
.loop-class(#list, (#index + 1));
}