57

In our app, we have a preset list of colors that a user can be choose from and everything related to that user will have that color.

Throughout the app, we will have various modules with the color attached as a class name.

eg.

<div class="example_module green">
  ...
</div>

We are using LESS for our CSS.

In a number of places we are doing things like this:

.example_module.green { background: @green; }
.example_module.red { background: @red; }
.example_module.blue { background: @blue; }
etc

I'd like to be able to set all these color names as an array and iterate over them. If we add colors in the future, we only have to add it in one place.

Pseudo code:

@colors: ['green', 'red', 'blue'];

for each @color in @colors {
  .example_module.@color { background: @color; }
} 

Is something like this even supported in LESS?

seven-phases-max
  • 11,765
  • 1
  • 45
  • 57
Jason Varga
  • 1,949
  • 2
  • 21
  • 29
  • It's possible in LESS using recursion. It's much more straightforward in SASS. Maybe you've heard of Google? ;^) – Stephen Thomas Jan 29 '14 at 19:37
  • 2
    SASS is not the way to go. I understand why it seems easier to do, but in the long run LESS or a LESS fork will when out purely on syntax adherence. – augurone Jun 11 '14 at 00:27

4 Answers4

88

See Loops. For example (assuming @green, @red, @blue variables are defined elsewhere):

@colors: green, red, blue;

.example_module {
    .-(@i: length(@colors)) when (@i > 0) {
        @name: extract(@colors, @i);
        &.@{name} {background: @@name}
        .-((@i - 1));
    } .-;
}

- - -

In Modern Less the same can be more straight-forward with the help of the Lists plugin:

@colors: green, red, blue;

.for-each(@name in @colors) {
    .example_module.@{name} {
        background: @@name;
    }
}

- - -

And in Legacy Less the syntactic sugar can be achieved using:

@import "for";

@colors: green, red, blue;

.example_module {
    .for(@colors); .-each(@name) {
        &.@{name} {background: @@name}
    }
}

Where the imported "for" snippet (it's just a wrapper mixin for recursive Less loops) can be found here (with examples here and here).

seven-phases-max
  • 11,765
  • 1
  • 45
  • 57
  • 2
    SyntaxError: Operation on an invalid type in common.less on line 6, column 3: ```5 .example_module { 6 .for(@colors); .-each(@color) { 7 @name: e(@color); ``` by lessc 1.7.5 – seyed Oct 23 '14 at 19:16
  • @seyed I'm afraid you need to create a new Q with complete example (this `for` thing is known to work fine in Less 1.5.x-2.0.x). – seven-phases-max Oct 24 '14 at 00:50
  • @seyed I was having that problem too. Mine was caused by a problem with the rhino-less integration; conflicting versions between the js.jar and less-rhino.x.x.x.js script. – nvioli Nov 11 '14 at 19:37
  • Hi, is it possible to use 2 for loops inside the same less file without having mixed classes generated? I mean, I have an array with colors and I generate background-colors rules and I want to generate my h1,h2,h3,h4,h5,h6 rules using a for too, but the problem is that less is also generating mixed classes that don't have any sense, taking both arrays and applying both for loops. – Fab Jan 12 '15 at 15:40
  • @Fab See https://github.com/seven-phases-max/less.curious/blob/master/articles/generic-for.md#multiple-loops-in-same-scope – seven-phases-max Jan 12 '15 at 16:12
  • @seven-phases-max thanks a lot, it worked. Can you explain me why? what does it mean the & {} wrapper and the .-() {} wrapper? – Fab Jan 12 '15 at 16:30
  • @Fab `& {}` is just an "unnamed namespace" (see [Parent Selectors](http://lesscss.org/features/#parent-selectors-feature)) and `.-() {}` is just an ordinary mixin named `.-`. – seven-phases-max Jan 12 '15 at 17:31
  • @seven-phases-max thanks for clarification. Do you know if it is possible to use functions like lighten and darken inside a for loop? Do you have any example? – Fab Jan 13 '15 at 09:18
  • @Fab Anything of Less can be used inside the loop - there's nothing special about it. (`.-each` is just an ordinary Less mixin so you can put whatever you need there). You'll find more example [here](http://codepen.io/seven-phases-max/public/). – seven-phases-max Jan 13 '15 at 18:59
  • @seven-phases-max this does not work with Less 2.6.1. Any idea why? – Morgan Feeney Mar 31 '16 at 14:02
  • @Morgan I did not face any problems with 2.6.1 so I assume you need to create a new ticket (as the problem is elsewhere). – seven-phases-max Mar 31 '16 at 21:52
  • Hi @seven-phases-max please see this question I just posted for you: http://stackoverflow.com/questions/36352716/passing-a-variable-list-through-for-loop-using-less-2-6-1-is-unable-to-compile – Morgan Feeney Apr 01 '16 at 09:31
  • 'for.less' wasn't found What to do? – TheCrazyProfessor Nov 21 '16 at 14:07
  • @TheCrazyProfessor Well, counting the link to the `for.less` *is* in the answer you probably need to read some beginners tutorial on what are CSS/Less imports and how to use them. – seven-phases-max Nov 22 '16 at 02:11
17

This mixin works fine for me. The second param is a code block that have access to the current iteration elem (@value) and index (@i).

  1. Define mixin:

    .for(@list, @code) {
        & {
            .loop(@i:1) when (@i =< length(@list)) {
                @value: extract(@list, @i);
    
                @code();
    
                .loop(@i + 1);
            }
    
            .loop();
        }
    }
    
  2. Use:

    @colors: #1abc9c, #2ecc71, #3498db, #9b59b6;
    
    .for(@colors, {
        .color-@{i} {
            color: @value;
        }
    });
    
  3. Result css:

    .color-1 {
      color: #1abc9c;
    }
    .color-2 {
      color: #2ecc71;
    }
    .color-3 {
      color: #3498db;
    }
    .color-4 {
      color: #9b59b6;
    }
    
Eduard Kolosovskyi
  • 1,396
  • 12
  • 18
  • Downvoting since it's not the answer to the question. You'd either need to modify your example to match the Q or find a more apropriate Q for your mixin library example (there're a lot of earlier and more general "how-to-loop" questions). – seven-phases-max Feb 22 '16 at 09:11
  • Also note though that DR based `.for-each` implementations were made since the day the DR-feature was added to Less, i.e. since 2014-02-27. And answering an old question just to post a link to yet another mixin library implementing something might not always be a good idea. (For your reference: [`1`](http://www.pnml.kz/2014/06/), [`2`](https://github.com/pixelass/homeless) etc. And finally see [#2270](https://github.com/less/less.js/issues/2270) for the summary of all possible sugar tricks by now). – seven-phases-max Feb 22 '16 at 09:26
13

With modern LESS (>= 3.7), you can use the built-in each function:

/* (assuming @clr-green, @clr-red, @clr-blue variables are defined elsewhere) */
@colors: {
  green: @clr-green;
  red: @clr-red;
  blue: @clr-blue;
}

each(@colors, {
  .example_module.@{key} {
    background: @value;
  }
});
eyecatchUp
  • 10,032
  • 4
  • 55
  • 65
0
  1. Define mixin:
.foreach(@list, @body, @i: length(@list)) when (@i>0) 
{
    .foreach(@list, @body, @i - 1);

    @n: length(@list);
    @value: extract(@list, @i);
    @body();
    /* you can use @value, @i, @n in the body */
}
  1. Usage:
.example-module {
  .foreach (red green blue,
  {
    &.@{value} {
      color: @value;
    }
  });
}

Another example:

.nth-child (@list, @style) {
    .foreach(@list, 
    {
      @formula: e(%("%dn+%d", @n, @i));
      &:nth-child(@{formula}) {
        @style();
      }
    });
}

tr {
  .nth-child (#bbb #ccc #ddd #eee,
  {
      background: @value;
  });
}