Loop over an array of name value pairs in LESS - less

Is there some way to loop an array of name/value pairs LESS? Something like this:
arr = alice: black, bob: orange;
.for(arr) // something something //
.cl-#{name} {
background-color: #{value}
}
To generate something like this:
.cl-alice { background-color: black; }
.cl-bob { background-color: orange; }
I know that you can for-loop an array, but I'm unsure if it can be an array of objects rather than values in LESS.

The answer given by #seven-phases-max works very well. For completeness you should also notice that you can do the same in Less without the imported "for" snippet.
from lesscss.org
In trying to stay as close as possible to the declarative nature of
CSS, Less has opted to implement conditional execution via guarded
mixins instead of if/else statements, in the vein of #media query
feature specifications.
and
In Less a mixin can call itself. Such recursive mixins, when combined
with Guard Expressions and Pattern Matching, can be used to create
various iterative/loop structures.
So in Less you could write:
#array: alice black, bob orange;
.createcolorclasses(#iterator:1) when(#iterator <= length(#array)) {
#name: extract(extract(#array, #iterator),1);
.cl-#{name} {
background-color: extract(extract(#array, #iterator),2);
}
.createcolorclasses(#iterator + 1);
}
.createcolorclasses();
or indeed:
#array: alice black, bob orange;
.createcolorclasses(#iterator:1) when(#iterator <= length(#array)) {
#name: extract(extract(#array, #iterator),1);
&#{name} {
background-color: extract(extract(#array, #iterator),2);
}
.createcolorclasses(#iterator + 1);
}
.cl-{
.createcolorclasses();
}

In Less a "pair" (in its simplest form) can be represented as an array too, so it can be as simple as:
#import "for";
#array: alice black, bob orange;
.for(#array); .-each(#value) {
#name: extract(#value, 1);
#color: extract(#value, 2);
.cl-#{name} {
background-color: #color;
}
}
Note however that the ".for" thing is limited to the only loop per scope so it's better to rewrite above to something like this:
#import "for";
#array: alice black, bob orange;
.cl- {
.for(#array); .-each(#value) {
#name: extract(#value, 1);
&#{name} {
background-color: extract(#value, 2);
}
}
}
The imported "for" snippet (it's just a wrapper mixin for recursive Less loops) can be found here (with examples here and here).

While it is useful from the other answers to know that Less supports recursive functions and mixins, there is now a much simpler answer to this simple question. This solution is tested to work with Less v3.9, but should work back to Less v3.7 when each was introduced.
.array() {
alice: black;
bob: orange;
}
each(.array(), {
.cl-#{key} {
background-color: #value;
}
});
The output is tidy:
.cl-alice {
background-color: black;
}
.cl-bob {
background-color: orange;
}
Want more? Well, as they say, "You can haz more". Use #index to use the 1-based index in the formula above.

Here is one "parametric mixins" which you can use with "key:value" pairs.
Array of "key:value" pairs is defined as follows: #array: "key:value", "key:value";
// imported "for" snippet (it's just a wrapper mixin for recursive Less loops)
// http://is.gd/T8YTOR
#import "for";
// loop all items and generate CSS
.generate_all(#array) {
.for(#array);
.-each(#item) {
#name: e(replace(#item, ':(.*)', ''));
#value: replace(#item, '^[^:]*:', '');
#{name} {
z-index: e(#value);
}
}
}
Definition:
#array_test: ".test:9000", "header .mainNav:9000", "footer:8000", "li.myclass:5000";
Test
.generate_all(#array);
Result:
.test {
z-index: 9000;
}
header .mainNav {
z-index: 8000;
}
footer {
z-index: 7000;
}
li.myclass {
z-index: 5000;
}
It works for me using grunt + less#1.7.4

Related

LESS: how to convert a list of called mixins to a for loop with a unique call

I crated many mixins to generate different kinds of classes for various purposes. Specifically I have to use a unique colorizer set using the standard bootstrap variable name, such as (only an example):
#type-primary: #fff;
#type-success: #f00;
#type-info: #ff0;
#type-default: #000;
#type-warning: #0f0;
#type-danger: #0ff;
Actually I created my mixins in the following form, with a "mother" as prefix to which I attached various suffixes
.text
{
&-primary { .color_text(#type-primary); }
&-success { .color_text(#type-success); }
&-info { .color_text(#type-info); }
&-default { .color_text(#type-default); }
&-warning { .color_text(#type-warning); }
&-danger { .color_text(#type-danger); }
}
After this, I can then create the final called mixin such as (so simple because it's only an example)
.color_text (#color)
{
color:#color;
}
I woud like to automate and optimize .text mixin to avoid many repeated rows, I think with a for loop. How could be possible?
Final results should be (in this case)
.text-primary {
color: #fff;
}
.text-success {
color: #f00;
}
.text-info {
color: #ff0;
}
.text-default {
color: #000;
}
.text-warning {
color: #0f0;
}
.text-danger {
color: #0ff;
}
In PSEUDO-CODE something like this could be ideal
#type-primary: #fff;
#type-success: #f00;
#type-info: #ff0;
.createContextClass("classNamePrefix",{#type-primary,#type-success,#type-info},mixinToBeCalled);
// Another call could be
.createContextClass("otherClassNamePrefix",{#type-primary,#type-success},otherMixinToBeCalled);
where, in relation to my original code, classNamePrefix should be the name of first part of final CSS class, then is passed an array with all kind of suffix that I wish in final CSS code, and mixinToBeCalled is the mixin that creates all css rules for final .text-primary, .text-success, .text-info.
For the moment, following Seven-Phases-Max' suggestion, I improved his solution in the following way

Declare variable in sass for color web changer [duplicate]

This question already has an answer here:
Define variables in Sass based on classes
(1 answer)
Closed 6 years ago.
i am making color changer for my web, is it possible to make variable like this :
.red { $color: red; $background: red; }
.green { $color: green; $background: green; }
.blue { $color: blue; $background: blue; }
thanks
There's nothing inherently wrong with your SASS here - at least in principle - but syntatically it's a tad skewed. Also, what your trying to do though requires so client side run-time code for it to be implemented.
First up though you don't actually need the variables - but we'll run with it. So change your sass to
$red: #ff1a1a;
$green: #5cd65c;
$blue: #1a75ff;
.blue { background-color: $blue; }
.green { background-color: $green }
.red { background-color: $red }
assuming this generates a CSS file and your importing this into your HTML page you'll need a little bit of Javascript to apply the appropriate colour class to the element you want to take on this property.
Assuming you have 3 elements ( buttons ) with unique ID's, which when clicked will change the background colour of an element id=foo you could have something like
var changeColor = function(col) {
document.getElementById("foo").className = col
}
document.getElementById('buttonblue').addEventListener('click',
function() {
changeColor('blue');
}, false);
document.getElementById('buttongreen').addEventListener('click',
function() {
changeColor('green');
}, false);
// ... etc etc for each color button you have
This is far from clean or modularised code, but hopefully it outlines the principle of the process which you need to follow
Here's a working codePen with the example: http://codepen.io/anon/pen/rewoOY

Simplifying Repetitive LESS

I am creating a themeing system for a WordPress network that supports multiple layout themes that can support color schemes for a variety of universities. To do so, I periodically compile a LESS file (using lessphp) with school-specific variables and essentially use it as a library of helper classes in the themes.
Each school has 3 colors defined in LESS as: #primary, #secondary and #tertiary. The method is straightforward and functional but requites a lot of repetition in the code. For example:
//Modifier Classes
.primary-lighter-text {
color: lighten(#primary,20);
}
.sec-lighter-text {
color: lighten(#secondary,20);
}
.tert-lighter-text {
color: lighten(#tertiary,20);
}
//Backgrounds
.primary-bg {
background-color: #primary;
}
.sec-bg {
background-color: #secondary;
}
.tert-bg {
background-color: #tertiary;
}
//Borders
.primary-border{
border-color: #primary;
}
.sec-border {
border-color: #secondary;
}
.tert-border {
border-color: #tertiary;
}
Nothing complicated from a LESS standpoint, but if I want to add a new helper class, I have to create 3. Is there a more succinct way to achieve this?
You can simplify it by making use of array loops. All you have to modify in case of a new addition would be to modify the array variables at the end.
.loop-column(#index) when (#index > 0) { /* Recursive Mixin with Guard condition. Mixin is processed only when the condition is satisfied */
.loop-column(#index - 1); /* Call the mixin again with a decremented counter */
#ctype: extract(#type, #index); /* Extract the type value corresponding to the index from the array */
#color: extract(#colors, #index); /* Extract the color value corresponding to the index from the array */
/* Form and Output the necessary classes and properties */
.#{ctype}-lighter-text { /* Selector interpolation to dynamically form the selector */
color: lighten(#color,20);
}
.#{ctype}-bg {
background-color: #color;
}
.#{ctype}-border{
border-color: #color;
}
}
.loop-column(length(#type));
#type: primary, sec, tert; /* The color types array */
#colors:#fff, #777, #000; /* The color value array for each type */
/* If required the colors can be kept as separate variables also. Refer 2nd demo. */
Demo | Demo 2
Update: (Based on comments from Andrew Cafourek and seven-phases-max)
Since LessPHP is outdated, the following line should be added and the length(#type) should be replaced with the actual count.
.loop-column(0) {};
.loop-column(4);

How do I make a list of CSS rules in LESS based on an unknown number of input arguments?

I'd like to make a LESS mixin for translating images like so:
.translate('/images/image.png', de, en-uk);
with an output that looks like this:
background-image: url('/images/image.png');
&:lang(de){ background-image: url('/images/image_de.png') }
&:lang(en-uk){ background-image: url('/images/image_en-uk.png') }
This would be easy if we were always translating the same number of languages, but unfortunately we are not (the content is the same across certain locales). I'm not sure how to make this number variable (which would future-proof the solution).
I guess what I'm looking for is a way to loop over each element in an array I pass and return another LESS rule for each.
Any ideas?
See Variadic mixin arguments, Loops, List Functions. For example it could be implemented somewhat like:
.test {
.translate('/images/image.png', grc, lat, san);
}
.translate(#image, #langs...) {
background-image: #image;
.-(length(#langs));
.-(#i) when (#i > 0) {
.-((#i - 1));
#lang: extract(#langs, #i);
&:lang(#{lang}) {
background-image: replace(#image, "\.", "_#{lang}.");
}
}
}
replace function requires Less 1.7.0 but for earlier versions you can use plain string interpolation/concatenation or format function as in #helderdarocha answer.
(Also note that the #langs... mixin parameter above can also accept the language list as a single variable), e.g.:
#languages: de, fr, es, ru, en-uk; // in fact commas here are optional too
.test {
.translate('/images/image.png', #languages);
}
And just in case, the same mixin using for wrapper (just to show that Less loops don't have to be that scary :):
#import "for";
.translate(#image, #langs...) {
background-image: #image;
.for(#langs); .-each(#lang) {
&:lang(#{lang}) {
background-image: replace(#image, "\.", "_#{lang}.");
}
}
}
This mixin uses target languages from a variable. It will loop through them and generate the code you want for each one:
.image-replace(#languages; #image-prefix) {
#count: length(#languages);
.loop(#count; #image-prefix);
.loop(#count; #image-prefix) when (#count > 0) {
.loop(#count - 1; #image-prefix);
#lang: extract(#languages, #count);
#image: %('%a_%a.png', #image-prefix, #lang);
&:lang(#{lang}){
background-image: url(#image);
}
}
}
To use it:
#languages: ~'de', ~'fr', ~'es', ~'ru', ~'en-UK', ~'pt-BR';
.section {
.image-replace(#languages; ~'/images/image');
}
Result:
.section:lang(de) {
background-image: url('/images/image_de.png');
}
.section:lang(fr) {
background-image: url('/images/image_fr.png');
}
.section:lang(es) {
background-image: url('/images/image_es.png');
}
.section:lang(ru) {
background-image: url('/images/image_ru.png');
}
.section:lang(en-UK) {
background-image: url('/images/image_en-UK.png');
}

LESS condition based on CSS class to set a LESS variable

I need to set a Less variable to match the website's active theme, ie, each theme has a different color.
I'd like to set #themeColor to the right color, based on the HTML's body CSS class that defines the theme.
For example:
body.themeBlue { #themeColor: blue; }
body.themeRed { #themeColor: red; }
This way I'd only need to use the #themeColor variable inside the other Less files.
Can anyone help?
According to this (http://www.lesscss.org/#-scope) it is possible to do something like that, but I can't make it work. what is going on here?
The LESS file cannot read the actual class applied to the html body element at run time (you would probably need to implement a javascript solution to do something like that).
If you just want to have all themed css ready for use based on the body class, the best way to implement this to have all the necessary theme based css in a mixin, then apply it under the theme classes. This reduces code duplication. For example:
LESS
//mixin with all css that depends on your color
.mainThemeDependentCss() {
#contrast: lighten(#themeColor, 20%);
h1 {color: #themeColor;}
p {background-color: #contrast;}
}
//use the mixin in the themes
body.themeBlue {
#themeColor: blue;
.mainThemeDependentCss();
}
body.themeRed {
#themeColor: red;
.mainThemeDependentCss();
}
CSS Output
body.themeBlue h1 {
color: #0000ff;
}
body.themeBlue p {
background-color: #6666ff;
}
body.themeRed h1 {
color: #ff0000;
}
body.themeRed p {
background-color: #ff6666;
}
For some other answers that deal with aspects or ways of theming, see:
LESS CSS - Change variable value for theme colors depending on body class
LESS.css variable depending on class
LESS CSS: abusing the & Operator when nesting?
Variables in Less are actually constants and will only be defined once.
Scope works within its code braces, so you would need to nest your CSS within each theme you want (which means duplication).
This is not ideal as you would need to do this:
body.themeBlue {
#color: blue;
/* your css here */
}
body.themeRed {
#color: red;
/* your css here AGAIN :( */
}
You could, however, try to use variables like this:
#color: black;
#colorRed: red;
#colorBlue: blue;
h1 {
color: #color; // black
body.themeRed & {
color: #colorRed; // red
}
body.themeBlue & {
color: #colorBlue; // blue
}
}
You would only need to declare the colours once, but you would need to constantly do the "body.themeRed" etc. prefixes where the colour varies depending on the theme.
You could actually use #import to load your theme! So common.less would contain all your default styles and #themeColor will be applied to it.
.mainThemeDependentCss() {
//file with all themed styles
#import 'common.less';
}
//use the mixin in the themes
body.themeBlue {
#themeColor: blue;
.mainThemeDependentCss();
}
body.themeRed {
#themeColor: red;
.mainThemeDependentCss();
}
BTW you should avoid using body selector in your common.less, because it wouldn't work.