LESS: Set class element to values in array - less

I have a class that is repeated multiple times. I'd like to change the text color of each class based on a list of colors that I define. If there are more elements than colors, the colors would need to start over. This is what I have so far, but it duplicates the colors for each element:
#colors: 'green', 'red', 'blue';
.view-priority-list-item-type {
.for(#colors); .-each(#color) {
color: #color;
}
}

As the example of #seven-phases-max already show you, you should try the Less list functions:
length() -> Returns the number of elements in a value list.
extract() -> Returns the value at a specified position in a list. Notice that the first position has index 1 and not 0
And also the mod() function.
Than you can do:
less
#elements: 'one','two','thee','four';
#colors: 'green', 'red', 'blue';
.classes(#i) when (#i > 0) {
.classes((#i - 1));
#classname: e(extract(#elements,#i));
.#{classname} {
color: e(extract(#colors, mod(#i - 1,length(#colors))+1));
}
}
.classes(length(#elements));
The above Less code will compile into the following CSS code:
.one {
color: green;
}
.two {
color: red;
}
.thee {
color: blue;
}
.four {
color: green;
}

Related

less compiler not work with extract function

after many and many test i can not understand why "extract" or "at" function in less not work.
i have tried use those functions in my less file, but without success.
file.less
#list: apple, pear, coconut , orange;
.test{
color:extract(#list,0);
}
#backgroundcolors:{
dark: #AA2222;
blue: #AA3333
}
#colors: {
bg-dark: #2f353b;
bg-blue: #3598dc
}
each(#backgroundcolors, {
.color-#{key}{
background-color: #value;
color: #index;
a:at(#colors,bg-blue);
b:at(#colors,"bg-blue");
c:extract(#colors,0);
}
});
file.css
.test {
color: extract(apple, pear, coconut, orange, 0);
}
.color-dark {
background-color: #AA2222;
color: 1;
a: at(, bg-blue);
b: at(, "bg-blue");
c: extract(, 0);
}
.color-blue {
background-color: #AA3333;
color: 2;
a: at(, bg-blue);
b: at(, "bg-blue");
c: extract(, 0);
}
in my result .test contains not only index 0, but any others item in #list. in sencod attempt i have trie use "at" or "extract" in a each loop, but always in this case i have failed. Now i use lessc 3.9.0
Where am I doing wrong?
Indexes for the extract function start from 1:
#list: apple, pear, coconut , orange;
a {
value: extract(#list, 1); // value: apple
}
Using of maps:
#colors: {
dark: #2f353b;
blue: #3598dc;
}
a {
color: #colors[dark]; // color: #2f353b
}
No idea what does at mean.

String interpolation for LESS Variables

I have a media query in LESS: #media (max-width: 1079px) { ... }. Basically I want to create a variable in LESS in which I can just say #media (*LESS variable goes here*) { ... } in my stylesheets. How can I interpolate the string max-width: 1079px into a LESS variable to make this happen?
Thanks
For the variable, you could escape the string value:
#media-max-width: ~"max-width: 1079px";
Code snippet:
#media-max-width: ~"max-width: 1079px";
#media (#media-max-width) {
p {
color: #f00;
}
}
Which will output the following:
#media (max-width: 1079px) {
p {
color: #f00;
}
}
You could also include the parenthesis in the variable too:
#media-max-width: ~"(max-width: 1079px)";
#media #media-max-width {
p {
color: #f00;
}
}

Less adds unexpected space to variable

For some reason, the output of nth child is rendered with un unexpected space. Can anyone help?
Renders:
// Part of render
body.domain-bsci-fta-local #block-domain-switcher ul li:nth-child( 3) {
background-color: #e14313;
}
From code:
// Variables
#a-primary: #018f9e;
#b-primary: #2b6a7c;
#c-primary: #e14313;
#d-primary: #009966;
#domain-a: 'a-local';
#domain-b: 'b-fta-local';
#domain-c: 'c-fta-local';
#domain-d: 'd-fta-local';
#domains: #domain-a #a-primary 1, #domain-b #b-primary 2, #domain-c #c-primary 3, #domain-d #d-primary 4;
// Call
body {
.generate-menus();
}
// Functions
.generate-menus() {
.for(#domains);
.-each(#domain) {
#dn: e(extract(#domain, 1));
#dc: extract(#domain, 2);
#dr: extract(#domain,3);
.generate-menu(#dn, #dc, #dr);
}
}
.generate-menu(#domainname, #domaincolor, #domaincount) {
&.domain-#{domainname} {
#block-domain-switcher {
ul {
li {
&:nth-child(#{domaincount}) {
background-color: #domaincolor;
a {
border-bottom: 5px solid;
color: white !important;
}
}
}
}
}
.navigation .submenu {
background-color: #domaincolor;
}
}
}
// ............................................................
// .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))
}
Note: As mentioned by seven-phases-max in his comments to the question, this was a bug which has already been fixed in v2.x. Leaving this answer (with the work-around solution) as-is to help future readers who can't upgrade their compiler for whatever reason.
The problem happens only for selectors which use selector interpolation and are nested within one or more parent selectors. It can be solved by using a temporary variable which contains the pseudo-selector like below: (it uses escaped string feature)
Option 1:
ul {
li {
#selector: ~":nth-child(#{domaincount})"; /* the selector is formed here */
&#{selector} { /* and used here */
background-color: #domaincolor;
a {
border-bottom: 5px solid;
color: white !important;
}
}
}
}
Option 2:
li {
#count: ~"(#{domaincount})";
&:nth-child#{count} { /* and used here */
background-color: #domaincolor;
a {
border-bottom: 5px solid;
color: white !important;
}
}
}
Sample Compiled Output:
body.domain-a-local #block-domain-switcher ul li:nth-child(1) {
background-color: #018f9e;
}
Related Links:
concatenate values in less (css) without a space
Redudant space in interpolated selectors like nth(...)
As mentioned above and in the linked issue thread, the issue happens only when the selector is formed using selector interpolation and is nested under one or more parents.
This works
// Variables
#list: a 1;
#num: extract(#list, 2);
// Usage
body div:nth-child(#{num}) {
color: #444;
}
But this doesnt
// Variables
#list: a 1;
#num: extract(#list, 2);
// Usage
body {
div:nth-child(#{num}) {
color: #444;
}
}

Less mixin calling another mixin won't generate nested rules

I have a Less project set up with three files relevant to my question.
A config file which just holds variables and their values.
A controller file which my Less watcher is told to compile.
A mixin file, titled _buttons.less, which I import as a reference in the
controller file.
Inside the controller file, I have a rule:
.bananas {
.nesting-mixin(#color-list);
}
Inside the variable file, I have a list:
#color-list : red, blue, green, black;
Inside the mixin file, I have two mixins:
.do-the-mixin(#list) {
color: extract(#list, 1);
&.blue {
color: extract(#list, 2);
}
&.green {
color: extract(#list, 3);
}
}
.nesting-mixin(#list) {
&.colored {
.do-the-mixin(#list);
}
}
When I call the .nesting-mixin method from the controller file, the output is:
.bananas.colored {
color: #ff0000;
}
But when I move the nesting mixin into the controller file, I get:
.bananas.colored {
color: #ff0000;
}
.bananas.colored {
color: #ff0000;
}
.bananas.colored.blue {
color: #0000ff;
}
.bananas.colored.green {
color: #008000;
}
Is there some aspect of Less mixin importing or nesting that I don't understand? The second output is what I want, but I don't understand why it needs to be in the same file from which it's called.
Thanks for reading. :)

How to pass a property name as an argument to a mixin in less

I want to make a function/mixin that will make a color darker if it is already dark but lighter when it is light (normalize/extremeize?)
Is it possible to do this by passing a property name (color, background-color, border-right-color, etc)?
.normalize(#color, #amount, #prop: "color") when (lightness(#color) >= 50%)
{
#prop:lighten(#color, #amount);
}
.normalize(#color, #amount, #prop: "color") when (lightness(#color) < 50%)
{
#prop:darken(#color, #amount);
}
This is currently a feature request on less.js github. So look out for it in less.js 1.4.. until then you can hack it like so...
.mixin(#prop, #value) {
Ignore: ~"a;#{prop}:#{value}";
}
Not very nice and you get an extra property but its the only way at the moment.
Guarded Mixins should be what you are looking for, however you can not use variables to define properties, only their values. So you can do it like this:
.normalize(#color, #amount) when (lightness(#color) >= 50%)
{
color:lighten(#color, #amount);
}
.normalize(#color, #amount) when (lightness(#color) < 50%)
{
color:darken(#color, #amount);
}
So this:
.class1 {
.normalize(#ddd, 10%);
}
Will output this:
.class1 {
color: #f7f7f7;
}
But you can not actually pass a property name as a variable. This is a limitation of LESS unfortunately, and while I've seen ways around it for things like margin direction, there is not a way to just pass any ol' property using a variable.
In the corresponding issue on Less' GitHub there is a workaround suggested by cloudhaed:
.blah () { color: black } // All blahs
.blah(right) { padding-right: 20px } // Right blahs
.blah(left) { padding-left: 20px } // Left blahs
#side: left;
.class { .blah(#side) }
Output
.class { color: black; padding-left: 20px;}
Maybe this will do?
This feature was added since v1.6.0:
#property: color;
.widget {
#{property}: #0ee;
background-#{property}: #999;
}
Compiles to:
.widget {
color: #0ee;
background-color: #999;
}
See http://lesscss.org/features/#variables-feature-properties