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));
}
Related
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"}
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";
}
});
I am currently converting code from SASS to LESS.
I have a problem with the following line of code:
&[data-rating = "#{counter - 0.5}"] { // THIS DOES NOT WORK
How can I work with variables and subtract 0.5 from my counter var and have it in a pair of quotes so that it can sit inside the HTML data attribute.
I have included two code examples so you can take the code and run it to see my results.
SASS:
.reviews-stars {
display: inline-block;
#for $i from 1 through 5 {
&[data-rating = "#{$i}"] {
.star:nth-child(-n + #{$i}):before {
color: green;
}
}
&[data-rating = "#{$i - 0.5}"] {
.star:nth-child(-n + #{$i}):before {
color: red;
}
}
}
}
LESS:
.looper-review-stars(#counter) when (#counter > 0) {
.looper-review-stars((#counter - 1)); // next iteration
&[data-rating = "#{counter}"] { // THIS WORKS
.star:nth-child(-n + #{counter}):before { // THIS WORKS
color: green;
}
}
// THIS DOES NOT WORK IT RETURNS THE STRING "#{counter - 0.5}"
&[data-rating = "#{counter - 0.5}"] { // THIS DOES NOT WORK
.star:nth-child(-n + #{counter}):before { // THIS WORKS
color: red;
}
}
}
.reviews-stars {
display: inline-block;
.looper-review-stars(5); // launch the loop
}
You can do it using a temporary variable like in the below snippet. Since selectors are strings I think it is better to keep all math operations away from it and in a separate statement.
.looper-review-stars(#counter) when (#counter > 0) {
.looper-review-stars((#counter - 1)); // next iteration
&[data-rating = "#{counter}"] {
.star:nth-child(-n + #{counter}):before {
color: green;
}
}
#temp: #counter - .5; /* temporary variable */
&[data-rating = "#{temp}"] {
.star:nth-child(-n + #{counter}):before {
color: red;
}
}
}
.reviews-stars {
display: inline-block;
.looper-review-stars(5); // launch the loop
}
I've extracted this piece of less code that is causing a web app to throw an error when it runs an asynchronous task to compile this less code. I've never written less before and from my initial perusal of the docs I can't seem to find whats wrong. Anybody know what's wrong?
This is the error:
ParseError: Unrecognised input in /Users/****/Desktop/testmoreless.css on line 4, column 3:
3
4 .core (#gridColumnWidth, #gridGutterWidth) {
5
This is the entire piece of code:
#grid {
.core (#gridColumnWidth, #gridGutterWidth) {
.spanX (#index) when (#index > 0) {
(~".span#{index}") { .span(#index); }
.spanX(#index - 1);
}
.spanX (0) {}
.offsetX (#index) when (#index > 0) {
(~".offset#{index}") { .offset(#index); }
.offsetX(#index - 1);
}
.offsetX (0) {}
.offset (#columns) {
margin-left: (#gridColumnWidth * #columns) + (#gridGutterWidth * (#columns - 1)) + (#gridGutterWidth * 2);
}
.span (#columns) {
width: (#gridColumnWidth * #columns) + (#gridGutterWidth * (#columns - 1));
}
.row {
margin-left: #gridGutterWidth * -1;
.clearfix();
}
[class*="span"] {
float: left;
margin-left: #gridGutterWidth;
}
// Set the container width, and override it for fixed navbars in media queries
.container,
.navbar-fixed-top .container,
.navbar-fixed-bottom .container { .span(#gridColumns); }
// generate .spanX and .offsetX
.spanX (#gridColumns);
.offsetX (#gridColumns);
}
.fluid (#fluidGridColumnWidth, #fluidGridGutterWidth) {
.spanX (#index) when (#index > 0) {
(~"> .span#{index}") { .span(#index); }
.spanX(#index - 1);
}
.spanX (0) {}
.span (#columns) {
width: (#fluidGridColumnWidth * #columns) + (#fluidGridGutterWidth * (#columns - 1));
}
.row-fluid {
width: 100%;
.clearfix();
> [class*="span"] {
float: left;
margin-left: #fluidGridGutterWidth;
}
> [class*="span"]:first-child {
margin-left: 0;
}
// generate .spanX
.spanX (#gridColumns);
}
}
.input(#gridColumnWidth, #gridGutterWidth) {
.spanX (#index) when (#index > 0) {
(~"input.span#{index}, textarea.span#{index}, .uneditable-input.span#{index}") { .span(#index); }
.spanX(#index - 1);
}
.spanX (0) {}
.span(#columns) {
width: ((#gridColumnWidth) * #columns) + (#gridGutterWidth * (#columns - 1)) - 10;
}
input,
textarea,
.uneditable-input {
margin-left: 0; // override margin-left from core grid system
}
// generate .spanX
.spanX (#gridColumns);
}
}
Take a look at this answer.
Quoting the answer verbatim:
This is what the file had originally:
(~".span#{index}") { .span(#index); }
After reading the LESS change log, I found that they changed the syntax so you can now use variables directly without needing the ~ hack. So I changed my mixin.less to look like this:
.span#{index} { .span(#index); }
There are a couple of other lines that you need to change, but they all follow the same format.
(~".offset#{index}") { .offset(#index); } changes to → .offset#{index} { .offset(#index); }
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);