The code below I have created to make a loop for a grid i'm setting up for my website.
On my CQ5.6.1 website it doesn't compile, but when I compile the code on less2css.org it works perfectly.
Adding this specific part in CQ5.6.1 the LESS compiler breaks the entire clientlibs. I know it's this specific part because if I just remove these lines everything works again.
So is there a limitation on the variables in CQ5.6.1 or something or does anyone have see what the issue is with this specific code
#containerwidth: 960;
#iterations: 12;
#margin-grid: 10;
#startwidth: 940; /* containerwidth - (margin-left + margin-right) */
#startprefix: #containerwidth;
#startsuffix: #containerwidth;
#startpush: #containerwidth;
#startpull: -#containerwidth;
.grid-loop (#index, #width, #prefix, #suffix, #push, #pull) when (#index > 0) {
.grid-#{index} {
display: inline;
float: left;
margin-left: unit(#margin-grid, px);
margin-right: unit(#margin-grid, px);
}
.push-#{index}, .pull-#{index} {
position: relative;
}
.container-12 {
.grid-#{index} {
width: unit(#width, px);
}
.prefix-#{index} {
padding-left: unit(#prefix, px);
}
.suffix-#{index} {
padding-right: unit(#suffix, px);
}
.push-#{index} {
left: unit(#push, px);
}
.pull-#{index} {
left: unit(#pull, px);
}
}
.grid-loop(#index - 1, #width - 80, #prefix - 80, #suffix - 80, #push - 80, #pull + 80);
}
.grid-loop (#iterations, #startwidth, #startprefix, #startsuffix, #startpush, #startpull);
Related
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";
}
});
Can anyone offer any solutions for combining encapsulation and mixin reuse for Less/CSS? I'm trying to keep my variables encapsulated by namespace, but I haven't figured out how to reuse mixins for it.
Example:
#signup-module {
#button-width: 100px;
#button-height: 30px;
#textfield-width: 300px;
#textfield-height: 20px;
.width( #value, #mod:0 ) {
width: ##value + #mod;
}
.height( #value, #mod:0 ) {
height: ##value + #mod;
}
}
.home-page-signup-module {
#signup-module > .width( button-width, -20px );
#signup-module > .height( button-height, -20px );
#signup-module > .width( textfield-width );
#signup-module > .height( textfield-height );
}
The problem is when I create a new module, the width() and height() mixins are repeated.
#contact-us-module {
#button-width: 50px;
#button-height: 20px;
#textfield-width: 300px;
#textfield-height: 20px;
.width( #value, #mod:0 ) {
width: ##value + #mod;
}
.height( #value, #mod:0 ) {
height: ##value + #mod;
}
}
Is there a way to maintain variable encapsulation and eliminate the mixin repetition? I'd like to write .width() and .height() once, but :extend() doesn't seem to work in this context.
Update: May 15, 2014
seven-phases-max offered a great solution below for reusing mixins, but I think I ran into a variable scope issue and the statement below returned an error. It said, "variable #textfield-width is undefined."
.home-page-signup-module {
.module-a.width(textfield-width, -20px);
}
So I tried adding .module-a which seems to work. I'm not 100% sure if this is correct usage but it does fix the error and return the correct value.
.home-page-signup-module {
.module-a;
.module-a.width(textfield-width, -20px);
}
You can collect shared mixins into another namespace/mixin and expand it in each "module" you need, something like this for example:
.shared-stuff() {
.width(#value, #mod: 0) {
width: ##value + #mod;
}
.height(#value, #mod: 0) {
height: ##value + #mod;
}
}
.module-a {
.shared-stuff();
#button-width: 100px;
#button-height: 30px;
#textfield-width: 300px;
#textfield-height: 20px;
}
.module-b {
.shared-stuff();
#button-width: 200px;
// etc.
}
// usage:
.home-page-signup-module {
.module-a.width(button-width, -20px);
.module-b.width(button-width, +33px);
}
I am using the following code to generate a column layout using LESS CSS:
.columnBuilder (#index) when (#index =< #columnCount) {
.container_#{columnCount} .grid_#{index} {
width: unit(((#baseWidth / #columnCount) * #index)-10, px);
}
.columnBuilder(#index + 1);
}
Which gives me an output:
.container_24 .grid_1 {
width: 69px;
}
.container_24 .grid_2 {
width: 148px;
}
.container_24 .grid_3 {
width: 227px;
}
etc...
How would i now create a new less function that would give an output of:
.grid_1,
.grid_2,
....,
.grid_N {
display: inline;
float: left;
margin-left: 5px;
margin-right: 5px;
}
Where N is defined as #columnCount: 24;, though the column count is not set, it can be changed to any number. I am aware i could create a body for each grid_X would like to avoid it to keep clutter down.
Using :extend() in LESS 1.4+
This seems to accomplish it more elegantly. You first define the initial values you will want extended in a hard coded .grid_1 class (at present, LESS will not extend dynamically generated classes), then add an extender mixin in your loop to extend to that class. Like so:
.grid_1 { //this acts as the "launch point" for extending them all
display: inline;
float: left;
margin-left: 5px;
margin-right: 5px;
}
.columnBuilder (#index) when (#index =< #columnCount) {
//we are going to use this class twice, so just calculate it once
#gridClass: ~'.grid_#{index}';
//this is your original code except the variable now used for the grid class
.container_#{columnCount} #{gridClass} {
width: unit(((#baseWidth / #columnCount) * #index)-10, px);
}
//this is your extender feature, which does not do so for the initial .grid_1
//which was set above as our launch point.
#{gridClass} {
.extender() when (#index > 1) {
&:extend(.grid_1 all);
}
.extender() when (#index = 1) {}
.extender();
}
//iterate the loop just as you were doing
.columnBuilder(#index + 1);
}
//call the loop starting at 1
.columnBuilder(1);
Output is your expected:
.grid_1,
.grid_2,
....,
.grid_N {
display: inline;
float: left;
margin-left: 5px;
margin-right: 5px;
}
As it turns out, LESS has no native support for something like this and will always create multiple blocks of CSS each with its own body, so you need to run a little hack with mixins. I used the following:
.columnBuilderX (#index) when (#index = 1) {
#isel: ~".grid_#{index}, ";
.columnBuilderX (#index + 1, #isel);
}
.columnBuilderX (#index, #innerSel) when (#index =< (#columnCount - 1)) {
#isel: #innerSel + ~".grid_#{index}, ";
.columnBuilderX (#index + 1, #isel);
}
.columnBuilderX (#index, #innerSel) when (#index = #columnCount) {
#isel: #innerSel + ~".grid_#{index} ";
#{isel} {
display: inline;
float: left;
margin-left: 5px;
margin-right: 5px;
}
.columnBuilderX (#index + 1, #isel);
}
Which produced for me:
.grid_1, .grid_2, .grid_3, .grid_4, .grid_5, .grid_6, .grid_7, .grid_8, .grid_9, .grid_10, .grid_11, .grid_12, .grid_13, .grid_14, .grid_15, .grid_16, .grid_17, .grid_18, .grid_19, .grid_20, .grid_21, .grid_22, .grid_23, .grid_24 {
display: inline;
float: left;
margin-left: 5px;
margin-right: 5px;
}
The first mixin is the initial mixin that is called that does not already have an inner selector, the second mixin requires a second param, which is the variable we create in the first, which then runs recursively until we hit our last column as defined by the when clause, where we add our last selector without the comma, and then use the selector list we have built in to apply our CSS too.
If anybody can come up with something simpler than this, please create an answer.
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);
The code we used is from the less file that's related to positioning. The website looks great in safari, firefox, and chrome and looks off in IE version 7, 8, and 9. Does bootstrap have a known issue with IE? Probably not since it's so widely used. But I can't really identify what's wrong. Btw, here are two sample pages with the visual bug in Internet Explorer: http://www.presspass.me and a simpler page: http://www.presspass.me/about or you can take a look at the screenshots.
My guess is that it's something small, any help would be appreciated!
/*
* Scaffolding
* Basic and global styles for generating a grid system, structural layout, and page templates
* ------------------------------------------------------------------------------------------- */
// Variables
// Can also be 24 / 20 / 20
// Or 16 / 40 /20
#gridColumns: 24;
#gridColumnWidth: 20px;
#gridGutterWidth: 20px;
#extraSpace: (#gridGutterWidth * 2); // For our grid calculations
#siteWidth: (#gridColumns * #gridColumnWidth) + (#gridGutterWidth * (#gridColumns - 1));
// Mixins
// Clearfix for clearing floats like a boss h5bp.com/q
.clearfix() {
zoom: 1;
&:before,
&:after {
display: table;
content: "";
zoom: 1;
}
&:after {
clear: both;
}
}
// Center-align a block level element
.center-block() {
display: block;
margin-left: auto;
margin-right: auto;
}
.fixed-container() {
width: #siteWidth;
margin-left: auto;
margin-right: auto;
.clearfix();
}
.columns(#columnSpan: 1) {
width: (#gridColumnWidth * #columnSpan) + (#gridGutterWidth * (#columnSpan - 1));
}
.offset(#columnOffset: 1) {
margin-left: (#gridColumnWidth * #columnOffset) + (#gridGutterWidth * (#columnOffset - 1)) + #extraSpace;
}
// Necessary grid styles for every column to make them appear next to each other horizontally
.gridColumn() {
display: inline;
float: left;
margin-left: #gridGutterWidth;
}
// makeColumn can be used to mark any element (e.g., .content-primary) as a column without changing markup to .span something
.makeColumn(#columnSpan: 1) {
.gridColumn();
.columns(#columnSpan);
}
// STRUCTURAL LAYOUT
// -----------------
/*
body {
margin: 0;
}
*/
// Container (centered, fixed-width layouts)
.container {
.fixed-container();
}
// Fluid layouts (left aligned, with sidebar, min- & max-width content)
.container-fluid {
position: relative;
min-width: 940px;
padding-left: 20px;
padding-right: 20px;
.clearfix();
> .sidebar {
position: absolute;
top: 0;
left: 20px;
width: 220px;
}
// TODO in v2: rename this and .popover .content to be more specific
> .content {
margin-left: 240px;
}
}
// BASE STYLES
// -----------
// Quick floats
.pull-right {
float: right;
}
.pull-left {
float: left;
}
// Toggling content
.hide {
display: none;
}
.show {
display: block;
}
// GRID SYSTEM
// -----------
// To customize the grid system, bring up the variables.less file and change the column count, size, and gutter there
.row {
.clearfix();
margin-left: -#gridGutterWidth;
}
// Find all .span# classes within .row and give them the necessary properties for grid columns (supported by all browsers back to IE7)
// Credit to #dhg for the idea
.row > [class*="span"] {
.gridColumn();
}
// Default columns
.span1 { .columns(1); }
.span2 { .columns(2); }
.span3 { .columns(3); }
.span4 { .columns(4); }
.span5 { .columns(5); }
.span6 { .columns(6); }
.span7 { .columns(7); }
.span8 { .columns(8); }
.span9 { .columns(9); }
.span10 { .columns(10); }
.span11 { .columns(11); }
.span12 { .columns(12); }
.span13 { .columns(13); }
.span14 { .columns(14); }
.span15 { .columns(15); }
.span16 { .columns(16); }
// For optional 24-column grid
.span17 { .columns(17); }
.span18 { .columns(18); }
.span19 { .columns(19); }
.span20 { .columns(20); }
.span21 { .columns(21); }
.span22 { .columns(22); }
.span23 { .columns(23); }
.span24 { .columns(24); }
// Offset column options
.row {
> .offset1 { .offset(1); }
> .offset2 { .offset(2); }
> .offset3 { .offset(3); }
> .offset4 { .offset(4); }
> .offset5 { .offset(5); }
> .offset6 { .offset(6); }
> .offset7 { .offset(7); }
> .offset8 { .offset(8); }
> .offset9 { .offset(9); }
> .offset10 { .offset(10); }
> .offset11 { .offset(11); }
> .offset12 { .offset(12); }
}
// Unique column sizes for 16-column grid
.span-one-third { width: 300px; }
.span-two-thirds { width: 620px; }
.row {
> .offset-one-third { margin-left: 340px; }
> .offset-two-thirds { margin-left: 660px; }
My guess is that it's something small, any help would be appreciated!
It is something small.
You're missing a doctype. Add as the very first line:
<!DOCTYPE html>
Without a valid doctype, your page is displayed in quirks mode.
Open the Developer Tools (press F12) to see which mode is actually being used.
Also sometimes you have to override group policies that may force IE into non compatibility mode. We have to do this at my organization because group policies force this mode on the intranet.
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
I had a similar issue with Bootstrap 2.2.1. It looked great in IE 9 where I was developing it. However, one of the users is on IE 7 (don't ask, ugh) and for the most part the header was not showing up correctly. The solution was to change all of the HTML 5 tags (header, footer, article and section) to divs. That did the trick for me.
I had tried just doing the above suggestions with DOCTYPE and such but nothing totally worked for me until that.