How do I create nested loops with LESS CSS? - css

What I know is that this:
#iterations: 8;
.mixin-loop (#index) when (#index > 0) {
.my-class-#{index} {
width: (100% / #index);
}
.mixin-loop(#index - 1);
}
.mixin-loop (0) {}
.mixin-loop(#iterations);
… Will result in this:
.my-class-8{width:12.5%}
.my-class-7{width:14.285714285714286%}
.my-class-6{width:16.666666666666668%}
.my-class-5{width:20%}
.my-class-4{width:25%}
.my-class-3{width:33.333333333333336%}
.my-class-2{width:50%}
.my-class-1{width:100%}
… Making it the LESS equivalent of:
for (var i = 8; i > 0; -- i) {
// …
}
My question is: What would the LESS equivalent of:
for (var i = 8; i > 0; -- i) {
for (var j = 4; j > 0; -- j) {
// …
}
}
… Look like?

Hm, nevermind—Found it myself.
I’m leaving the answer here for posterity’s sake:
#maxi: 8;
.i-loop (#i) when (#i > 0) {
#maxj: 8;
.j-loop (#j) when (#j > 0) {
.my-class-#{i}-#{j} {
width: (100% / #i);
height: (100% / #j);
}
.j-loop(#j - 1);
}
.j-loop (0) {}
.j-loop(#maxj);
.i-loop(#i - 1);
}
.i-loop (0) {}
.i-loop(#maxi);

An Unnested Solution
I'm only offering this as an alternative here for final output code purposes. My answer does not really address nesting of loops directly (as your question is and your own answer found that solution). Rather, it challenges whether nesting is even best to solve the problem you faced.
Assuming a class structure just as you have (say my-class-2-6 for example), you can reduce from 64 output CSS selectors to just 16 by not nesting them and instead using CSS3 attribute selectors (which may not be desirable, depending on target browsers you need to support). Thus this LESS:
#maxi: 8;
#maxj: 8;
#iSelStart: ~'[class^=my-class-';
#jSelStart: ~'[class$=-';
#ijSelEnd: ~']';
.i-loop (#i) when (#i > 0) {
#{iSelStart}#{i}#{ijSelEnd} {
width: (100% / #i);
}
.i-loop(#i - 1);
}
.j-loop (#j) when (#j > 0) {
#{jSelStart}#{j}#{ijSelEnd} {
height: (100% / #j);
}
.j-loop(#j - 1);
}
//stop loops
.i-loop (0) {}
.j-loop (0) {}
//itialize loops
.j-loop(#maxj);
.i-loop(#maxi);
Becomes this CSS:
[class$=-8] {
height: 12.5%;
}
[class$=-7] {
height: 14.285714285714286%;
}
[class$=-6] {
height: 16.666666666666668%;
}
[class$=-5] {
height: 20%;
}
[class$=-4] {
height: 25%;
}
[class$=-3] {
height: 33.333333333333336%;
}
[class$=-2] {
height: 50%;
}
[class$=-1] {
height: 100%;
}
[class^=my-class-8] {
width: 12.5%;
}
[class^=my-class-7] {
width: 14.285714285714286%;
}
[class^=my-class-6] {
width: 16.666666666666668%;
}
[class^=my-class-5] {
width: 20%;
}
[class^=my-class-4] {
width: 25%;
}
[class^=my-class-3] {
width: 33.333333333333336%;
}
[class^=my-class-2] {
width: 50%;
}
[class^=my-class-1] {
width: 100%;
}
So the example of my-class-2-6 would target the start of the class name my-class-2 giving a width: 50% and target the end of the class name -6 which would give a height: 16.666666666666668%;.
Just a thought for any future users facing a similar situation who are only worried about targeting CSS3 browsers.
Update: Added Protection to not Incorrectly Target
As an after thought, it occurred to me that if you have various types of classes that may have an ending of -1 or -2 etc., then your ending CSS may need to have an additional set of code to help filter for just that class. So the j loop code above would need to have a change to the selector string like so:
#{iSelStart}#{ijSelEnd}#{jSelStart}#{j}#{ijSelEnd} { /*properties*/}
Which would then output this format of code:
[class^=my-class-][class$=-1] {
/*properties*/
}
This way it is looking specifically for the my-class- "class" that ends in -1, and would ignore selecting another class like another-class-1 as the original code above would still select. Whether this is an issue or not would purely be related to the design and class naming used in one's site.

Old question but maybe it's worth mentioning that Less can now do this easier
Function
.for(#i, #n, #r){#r();._(#i)}
.for(#n, #r)when(isnumber(#n)){.for(0, #n, #r)}
.for(#i, #n, #r)when not(#i = #n - 1){.for((#i + ((#n - #i) / abs(#n - #i))), #n, #r)}
Usage
.for(3, {._(#i) {
.for(3, {._(#j) {
item-#{i}-#{j} {
i: #i;
j: #j;
}
}});
}});
Example: Codepen

.loop(#n: 1, #m: #n, #k: #n * #m) when(#k > 0) {
.loop(#n, #m, #k - 1);
#i: `Math.floor((#{k} - 1) / #{m})`;
#j: #k - #i * #n - 1;
/*
#i runs up 1 to #n,
#j runs up 1 to #m and
#k runs up 1 to #n * #m
for example:
*/
&:nth-child(#{k}) {
top: 50px * #i;
left: 100px * #j;
}
}
/* using: */
.loop(3,4);

Related

Generating the value of CSS content in LESS loop

We have a CSS style that adds dots before and after a heading indicating the nesting level of the heading:
.heading.nesting-1:before,.heading.nesting-1:after{content:"\00B7"}
.heading.nesting-2:before,.heading.nesting-2:after{content:"\00B7\00B7"}
.heading.nesting-3:before,.heading.nesting-3:after{content:"\00B7\00B7\00B7"}
...
I have tried the following LESS but it doesn't concatenate the strings correctly
#entity-dot: '\00B7';
#nesting-levels: 9;
.heading {
.nesting-loop (#i) when (#i <= #nesting-levels) {
&.nesting-#{i} {
&:before, &:after {
.nested-dots(#j, #current: "") when (#j > 0) {
#nested-dots: "#{current}#{entity-dot}";
.nested-dots((#j - 1), #nested-dots);
}
.nested-dots(#j: #i, "");
content: "#{nested-dots}";
}
}
.nesting-loop(#i + 1);
}
.nesting-loop (1);
}
The result is:
.heading.nesting-1:before,.heading.nesting-1:after{content:"\00B7"}
.heading.nesting-2:before,.heading.nesting-2:after{content:"\00B7"}
.heading.nesting-3:before,.heading.nesting-3:after{content:"\00B7"}
...
I think I understand that the problem is that the property #nested-dots cannot be iteratively updated in this way but I was wondering if anybody had a solution?
Thanks for your help.
This is how I resolved it with the help of this post
(thank you Martin Turjak)
#nesting-levels: 5;
#entity-space: '\00A0';
#entity-dot: '\00B7';
.content-dots (#numdots) {
//Mixin output
.nested-dots(1, #dots1) {
#dots: "#{dots1}#{entity-dot}";
}
//Mixin iterator
.nested-dots(#index, #dots1) when (#index > 1) {
#dots2: "#{dots1}#{entity-dot}#{entity-space}";
.nested-dots((#index - 1), #dots2);
}
//Call mixin
.nested-dots(#numdots, "");
//Output dots
content: "#{dots}";
}
.heading {
.nesting-loop (#i) when (#i >= 1) {
&.nesting-#{i} {
&:before, &:after {
.content-dots(#i);
}
}
.nesting-loop (#i - 1);
}
.nesting-loop (#nesting-levels);
}
The result is
.heading.nesting-5:before,.heading.nesting-5:after{content:"\00B7\00A0\00B7\00A0\00B7\00A0\00B7\00A0\00B7"}
.heading.nesting-4:before,.heading.nesting-4:after{content:"\00B7\00A0\00B7\00A0\00B7\00A0\00B7"}
.heading.nesting-3:before,.heading.nesting-3:after{content:"\00B7\00A0\00B7\00A0\00B7"}
.heading.nesting-2:before,.heading.nesting-2:after{content:"\00B7\00A0\00B7"}
.heading.nesting-1:before,.heading.nesting-1:after{content:"\00B7"}

How can I use a loop in LESS to create specific class names for typography?

I want to generate 9 typography classes, each with the following:
font-size: 2rem;
line-height: 1rem;
I'll be using standard typographic multipliers for font sizes and line-height. Instead of hard-coding all of these CSS classes, I was wondering if there was a more elegant way of generating them in a loop using LESS.
I found the following from another thread:
#iterations: 5;
.span-loop (#i) when (#i > 0) {
.span-#{i} {
width: ~"#{i}%";
}
.span-loop(#i - 1);
}
.span-loop (#iterations);
Which generates:
.span-5 {
width: 5%;
}
.span-4 {
width: 4%;
}
.span-3 {
width: 3%;
}
.span-2 {
width: 2%;
}
.span-1 {
width: 1%;
}
This is pretty close, but I'd love for my class names to more "named". How can I use a loop to generate classes for:
.small { }
.caption { }
.body { }
.subheader { }
.title { }
.headline { }
etc...
I'm also not tied to LESS, so if there's a better CSS preprocessor language, then I'm happy to use that instead :)
Thank you!
An example from documentation for further modification;)
for more complicated code, it is better to use scss than less
.for(#list, #code) {
& {
.loop(#i: 1) when (#i =< length(#list)) {
#value: extract(#list, #i);
#code();
.loop(#i + 1);
}
.loop();
}
}
#elements: small, caption, body, subheader, title, headline;
.for(#elements, {
#remfont: #i+1;
#remline: ((#i+1) * 1.5 / 3);
.#{value} {
font-size: ~"#{remfont}rem";
line-height: ~"#{remline}rem";
}
});

Playing with LESS - optimize css generated by passing ruleset to mixin

I'm just playing with LESS lately. I wanted to generate rules for elements with numeric ID. At some stage I got code like this:
#myRule: {padding: 0;};
.myLoop(#c, #rules) when (#c >= 0) {
.myLoop((#c - 1), #rules);
&[id*=#{c}] { #rules(); }
&[name*=#{c}] { #rules(); }
}
.myClass {
.myLoop(2, #myRule);
}
which compiles to
.myClass[id*=0] {
padding: 0;
}
.myClass[name*=0] {
padding: 0;
}
.myClass[id*=1] {
padding: 0;
}
.myClass[name*=1] {
padding: 0;
}
.myClass[id*=2] {
padding: 0;
}
.myClass[name*=2] {
padding: 0;
}
My question is: can I in any way make it compile to sth like this:
.myClass[id*=0],
.myClass[name*=0],
.myClass[id*=1],
.myClass[name*=1],
.myClass[id*=2],
.myClass[name*=2] {
padding: 0;
}
I was looking for something like 'extending mixins`, 'parametric extend' or 'extending ruleset' but all lead to issues that are either 'wontfix' or 'nice-to-have' :-) So I guess it's not yet possible, but I would just like to reach out to people more familiar with less then I am, to be sure.
Yes, neither extending parametric mixins nor scoped extend are possible currently, so the easiest method to achieve the result is to extend a dummy ruleset. E.g.:
.my-repeat(#i, #f) when (#i >= 0) {
.my-repeat((#i - 1), #f);
&[id*=#{i}], &[name*=#{i}] {#f();}
}
.my-class-style {
padding: 0;
}
.my-class {
.my-repeat(2, {
&:extend(.my-class-style);
});
}
where .my-class-style is the dummy selector to appear in the resulting CSS too.

scss for loop renders percentage wierd

I'm trying to make a responsive design that flips from horizontal to vertical depending on the screen width and I need all the widths in percentage defined in css because I cannot use JavaScript.
The app is based on Middleman (a ruby gem) and I use scss as the css processor/renderer.
For this in scss:
#for $i from 0 through 100 {
.width-percentage-#{$i} {
width : #{percentage($i/100)};
}
}
It gets this in css :
...
.width-percentage-27 {
width: 27%
}
.width-percentage-28 {
width: 28.0%
}
.width-percentage-29 {
width: 29.0%
}
.width-percentage-30 {
width: 30%
}
...
.width-percentage-53 {
width: 53%
}
.width-percentage-54 {
width: 54%
}
.width-percentage-55 {
width: 55.0%
}
.width-percentage-56 {
width: 56.0%
}
.width-percentage-57 {
width: 57.0%
}
.width-percentage-58 {
width: 58.0%
}
.width-percentage-59 {
width: 59%
}
...
Now notice the problem ... for 28, 29,55, 56, 57, 58 there is a .0 in the value.
The browsers I'm targeting are handling this fine. But how does scss processor renders somehow different value types (int and floats) in the same loop ?
Strange indeed you could solve this issue by using round or ceil
#for $i from 0 through 100 {
.width-percentage-#{$i} {
width : round(percentage($i/100));
}
}
An example: http://sassmeister.com/gist/2c6f6b7cd825b16e423a
You only need to add % symbol to 100 to make the operation work.
#for $i from 1 through 100 {
.width-percentage-#{$i} {
width : #{100% / $i};
}
}

less css grid settup

I'm creating less grid sistem and i dont know how to optimise grid.less
If i chouse #gridColumns: 14; i need to add new lines in grid.less, maybe there is anothes option to automate this?
Variables.less
#gridColumns: 12;
#gridWidth: 62.5em;
#gridGutterWidth: 1.8em;
#grid.less
.l-1 { .grid(1); }
.l-2 { .grid(2); }
.l-3 { .grid(3); }
.l-4 { .grid(4); }
.l-5 { .grid(5); }
.l-6 { .grid(6); }
.l-7 { .grid(7); }
.l-8 { .grid(8); }
.l-9 { .grid(9); }
.l-10 { .grid(10); }
.l-11 { .grid(11); }
.l-12 { .grid(12); }
#mixins.less
.grid(#num) {
width: (100% / #gridColumns) * #num;
position: relative;}
The only sensible way of doing it using LESS is using a recursive mixin like Twitter Bootstrap.
.spanX (#index) when (#index > 0) {
.span#{index} { .span(#index); }
.spanX(#index - 1);
}
.spanX (0) {}
.span (#columns) {
width: (#gridColumnWidth * #columns) + (#gridGutterWidth * (#columns - 1));
}

Resources