OK, let's say that I have a Less file like so:
// Turquoise
#turquoise: #1ABC9C;
#turquoise-2: #16A085;
// Greens
#green: #2ECC71;
#green-2: #27AE60;
// Blues
#blue: #3498DB;
#blue-2: #2980B9;
// Navy Blues
#navy: #34495E;
#navy-2: #2C3E50;
// Purples
#purple: #9B59B6;
#purple-2: #8E44AD;
// Yellows
#yellow: #F1C40F;
// Oranges
#orange: #F39C12;
#orange-2: #E67E22;
#orange-3: #D35400;
// Reds
#red: #E74C3C;
#red-2: #C0392B;
// Greys
#grey: #ECF0F1;
#grey-2: #BDC3C7;
#grey-3: #95A5A6;
#grey-4: #7F8C8D;
#colour-scheme:
#red, #red-2,
#orange, #orange-2, #orange-3,
#yellow,
#green, #green-2,
#turquoise, #turquoise-2,
#blue, #blue-2,
#purple, #purple-2,
#navy, #navy-2,
#grey, #grey-2, #grey-3, #grey-4
;
And I wanted to make a mixin that went like so:
.createBG( #colour-array: #colour-scheme, #n: length( #colour-scheme ), #i: 0 ){
&.<[COLOUR-ARRAY[#i] VARIABLE NAME]>{
background-color: extract( #colour-array, #i );
}
.createBG( #colour-array, ( #i + 1 ) )
}
How would I achieve to replace <[COLOUR-ARRAY[#i] VARIABLE NAME]> with valid Less code that would give me the variable name of the current index of the list/array.
What would be the surefire, and best practice, way of implementing this?
You can do this using the extract function itself by just setting the color variable names as value for the #color-scheme variable instead of giving it the variable itself. Within the mixin, ## can be used to resolve the variable's name to its actual color value.
#colour-scheme:
red, red-2, orange, orange-2, orange-3, yellow, green, green-2, turquoise, turquoise-2,
blue, blue-2, purple, purple-2, navy, navy-2, grey, grey-2, grey-3, grey-4;
.createBG(#colour-array: #colour-scheme, #n: length(#colour-scheme), #i: 1 ) when (#i <= #n){
#color: extract(#colour-array, #i);
&.#{color}{
background-color: ##color;
}
.createBG( #colour-array, #n, #i + 1 )
}
.createBG();
Since you need the color names also (to be the class name), you'd definitely have to store the names in some variable and so there is not much reduction possible. But we could still use associative array. A sample for associative array is provided below.
#colour-scheme:
red #E74C3C, red-2 #C0392B,
orange #F39C12, orange-2 #E67E22, orange-3 #D35400,
yellow #F1C40F,
green #2ECC71, green-2 #27AE60,
turquoise #1ABC9C, turquoise-2 #16A085,
blue #3498DB, blue-2 #2980B9,
purple #9B59B6, purple-2 #8E44AD,
navy #34495E, navy-2 #2C3E50,
grey #ECF0F1, grey-2 #BDC3C7, grey-3 #95A5A6, grey-4 #7F8C8D;
.createBG(#colour-array: #colour-scheme, #n: length(#colour-scheme), #i: 1 ) when (#i <= #n){
#color-name-hex: extract(#colour-array, #i);
#color-name: extract(#color-name-hex, 1);
#color-hex: extract(#color-name-hex, 2);
&.#{color-name}{
background-color: #color-hex;
}
.createBG( #colour-array, #n, #i + 1 )
}
.createBG();
Note: Have a look at this bug in v2.6.1 and it could end up spoiling your loops. It will be fixed though.
Related
I'm working with LESS version 1.6.0 (not by choice). I read through the LESS documentation on Mixin Guards. I created a LESS function to cycle through a key-value list of colors, to generate CSS classes. Within this function, I want to set a condition to change the properties of my styles, if my prefixes are different. (i.e. txt and bg should have the same CSS declaration, while border has a different one). I believe my code below "is" doing that, but its also compounding my CSS properties for each class. Here is what I tried...
/*MY COLORS*/
#black-n25: #bfbfbf;
#black-n75: #4d4d4d;
#black: #000;
#white: #fff;
/*MY LIST (KEY VALUE PAIRS)*/
#colors: ~'black-n25' #black-n25, ~'black-n75' #black-n75, ~'black' #black, ~'white' #white;
/*MY FUNCTION TO CYCLE THROUGH MY LIST AND GENERATE CLASSES*/
.generate-classes(#list, #prefix, #mprop) {
.iter(length(#list));
.iter(#i) when (#i > 0) {
.iter(#i - 1);
#pair: extract(#list, #i); /*#colors -> #list -> #pair*/
#key: extract(#pair, 1); /*the key from #colors above*/
#value: extract(#pair, 2); /*the value from #colors above*/
/*this is where things go wrong...*/
.mixin(#prefix) when (#prefix = txt), (#prefix = bg) {
#{mprop}: #value;
}
.mixin(#prefix) when (#prefix = border) {
#{mprop}: solid 2rem #value;
}
.#{prefix}-#{key} {
.mixin(#prefix);
}
}
}
/*CALL FUNCTION FOR EACH CASE | PARAMETERS ARE...THE LIST, PREFIX (CSS SELECTOR NAME), AND CSS PROPERTY VALUE*/
.generate-classes(#colors,txt,color);
.generate-classes(#colors,bg,background-color);
.generate-classes(#colors,border,border);
...and a visual of what's being generated in the browser...
As you can see its compounding declarations for each class except the first class. Each CSS class should only have one declaration. What am I doing wrong?
This took some further digging. but now I can see, as of LESS version 1.5.0, CSS Guards can help when iterating through a key-value paired list items in LESS. I removed my use of Mixin Guards from my original logic and instead have the following setup, which now works.
This now gives me the flexibility to add many more colors and custom CSS declarations for custom prefixes, all within a single function.
/*MY COLORS*/
#black-n25: #bfbfbf;
#black-n75: #4d4d4d;
#black: #000;
#white: #fff;
/*MY LIST (KEY VALUE PAIRS)*/
#colors: ~'black-n25' #black-n25, ~'black-n75' #black-n75, ~'black' #black, ~'white' #white;
/*MY FUNCTION TO CYCLE THROUGH MY LIST AND GENERATE CLASSES*/
.generate-classes(#list, #prefix, #mprop) {
.iter(length(#list));
.iter(#i) when (#i > 0) {
.iter(#i - 1);
#pair: extract(#list, #i); /*#colors -> #list -> #pair*/
#key: extract(#pair, 1); /*the key from #colors above*/
#value: extract(#pair, 2); /*the value from #colors above*/
/*this now works and sets a single declaration for each class...*/
.#{prefix}-#{key} when (#prefix = txt), (#prefix = bg) {
#{mprop}: #value;
}
.#{prefix}-#{key} when (#prefix = border) {
#{mprop}: solid 2rem #value;
}
}
}
/*CALL FUNCTION FOR EACH CASE | PARAMETERS ARE...THE LIST, PREFIX (CSS SELECTOR NAME), AND CSS PROPERTY VALUE*/
.generate-classes(#colors,txt,color);
.generate-classes(#colors,bg,background-color);
.generate-classes(#colors,border,border);
I'm looking at the bootstrap LESS source code and there is the following piece there:
.calc-grid-column(#index, #class, #type) when (#type = width) and (#index > 0) {
.col-#{class}-#{index} {
width: percentage((#index / #grid-columns));
}
}
Why do they write -#{class} instead of simply -#class?
As already explained by #Damien_The_Unbeliever Less enables you to use selector names, property names, URLs and #import statements. (also attribute selectors?).
The curly braces {} are used to make the variable name clear. In the case of #ab you can read variable #a or #ab. #{a}b forces that the variable #a will be used and has a string b appended.
I realise this is a similar question to this Conditional CSS based on background color variable
but I need to do it inside a loop in LESS. If a background colour is too dark I want to generate another class so I can make the text on top lighter but not sure how as I don't think you can use lighten and darken functions with hex colours...?
Here is my LESS http://codepen.io/anon/pen/IlsJE?editors=110
.for(#i, #n) {.-each(#i)}
.for(#n) when (isnumber(#n)) {.for(1, #n)}
.for(#i, #n) when not (#i = #n) {
.for((#i + (#n - #i) / abs(#n - #i)), #n);}
// .for-each
.for(#array) when (default()) {.for-impl_(length(#array))}
.for-impl_(#i) when (#i > 1) {.for-impl_((#i - 1))}
.for-impl_(#i) {.-each(extract(#array, #i))}
// PAs
#pa1: "pa1";
#pa2: "pa2";
#pa3: "pa3";
#pa4: "pa4";
// Colors
#pa1-color: #72afb6;
#pa2-color: #9fad9f;
#pa3-color: #8dd8f8;
#pa4-color: #00567A;
// Setting variables and escaping them
#pas: ~"pa1" ~"pa2" ~"pa3" ~"pa4";
// Define our variable
.define(#var) {
#pa-color-primary: '#{var}-color';
}
// Starting the PA mixin
.pacolors() {
// Generating the loop for each PA
.for(#pas); .-each(#name) {
// After loop happens, it checks what brand is being called
.define(#name);
.#{name} .bg-accent {
background-color: ##pa-color-primary;
}
}
}
.pacolors();
Any help would be appreciated.
You can achieve this by using the built-in contrast function provided by LESS.
// Starting the PA mixin
.pacolors() {
// Generating the loop for each PA
.for(#pas); .-each(#name) {
// After loop happens, it checks what brand is being called
.define(#name);
.#{name} .bg-accent {
background-color: ##pa-color-primary;
color: contrast(##pa-color-primary,
lighten(##pa-color-primary, 100%),
darken(##pa-color-primary, 100%),10%);
/* syntax - contrast(color-for-comparison,
color1 - lighten (100%) is essentially white,
color 2 - darken (100%) is essentially black,
threshold percentage based on which decision is taken
*/
}
}
}
Demo | LESS Function Spec - Contrast
Simplified Version: (Courtesy - seven-phases-max)
// Colors
#pas:
pa1 #72afb6,
pa2 #9fad9f,
pa3 #8dd8f8,
pa4 #00567A;
// Styles
& {
.for(#pas); .-each(#pa) {
#name: extract(#pa, 1);
#color: extract(#pa, 2);
.#{name} .bg-accent {
background-color: #color;
color: contrast(#color, white, black, 10%);
}
}
}
p {padding: 10px}
// ........................................................
#import "https://raw.githubusercontent.com/seven-phases-max/less.curious/master/src/for";
Demo 2
I have icon sprites image with each icon in a 20 by 20 pixel area. Each icon has several variants (black, colour, white small etc.). And have a significant amount of them. Instead of writing styles for each individual icon I'd rather just provide their names in my LESS file and let the processor generate styles for them.
This is what I came up with but it doesn't seem to work.
#icons: upvote,downvote,comment,new,notify,search,popup,eye,cross;
#array: ~`(function(i){ return (i + "").replace(/[\[\] ]/gi, "").split(","); })("#{icons}")`;
#count: ~`(function(i){ return i.split(",").length; })("#{icons}")`;
.iconize (#c) when (#c < #count) {
#val: ~`(function(a){ return a.replace(" ","").split(",")[0]; })("#{array}")`;
#array: ~`(function(a){ a = a.replace(" ","").split(","); a.splice(0, 1); return a; })("#{array}")`;
&.#{val} { background-position: (-20px * #c) 0; }
&.color.#{val} { background-position: (-20px * #c) -20px; }
&.white.#{val} { background-position: (-20px * #c) -40px; }
.iconize(#c + 1);
}
.iconize(#c) when (#c = #count) {}
.iconize(0);
The only thing I'd like to edit is the #icons variable where I just enter their names. And I'm using Web Essentials addin for Visual Studio 2013 to automatically process my LESS file on file save.
What am I doing wrong?
Pure LESS (assuming you're using Web Essentials 2013 which uses LESS 1.5.x):
#icons: upvote, downvote, comment, new, notify, search, popup, eye, cross;
.iconize();
.iconize(#i: length(#icons)) when (#i > 0) {
.iconize((#i - 1));
#value: extract(#icons, #i); // LESS arrays are 1-based
.#{value} {background-position: (-20px * (#i - 1)) 0}
.color.#{value} {background-position: (-20px * (#i - 1)) -20px}
.white.#{value} {background-position: (-20px * (#i - 1)) -40px}
}
I removed & from selector names since it has no effect when you generate these classes in the global scope (but put it back if you actually need .iconize to be nested in another ruleset). It is also possible to calculate array length in earlier LESS versions (that have no length function) w/o any javascript, but I don't list this method here since it's quite scary (and you don't need it anyway).
Your javascript based loop is in fact less or more correct but the problem is all values returned by LESS inline javascript are of so-called "anonymous value" type and not a numbers so that when (#c < #count) condition is always true and the loop becomes infinite. (basically the condition is expanded exactly as when (0 < ~'9') ... when (9 < ~'9') = true etc.)
I think it depends on the version of LESS you use. Different versions of LESS handle array like structures and their length different.
Since LESS 1.5 you can define an array with quotes, like:
#array: "value1","value2"; and calculate its length with length(#array).
For example see also:
Sprites LESS CSS Variable increment issue
With LESS 1.5 your code ends in an endless loop: "SyntaxError: Maximum call stack size exceeded in"
The interface should look like this
+box(optional_padding_value_in_columns)
It shouldn't break the grid. (If placed in column that spans 7 units, then the box should stay within the 7 units.)
Compass _scaffolding.sass actually includes this little number:
// Mixin +box to create a padded box inside a column.
=box
:padding 1.5em
:margin-bottom 1.5em
:background #E5ECF9
But the padding blows up the grid.
The +box mixin is meant for a block element used inside a column. The 1.5em padding is not proportional to the blueprint column width so putting it on a column element will break the layout.
Try this:
// You can supply one padding value or all 4
=padded_column(!n, !p1, !p2=!p1, !p3=!p1, !p4=!p1)
+column(!n)
:padding= !p1 !p2 !p3 !p4
!width = !blueprint_grid_width * !n + (!blueprint_grid_margin * (!n - 1)) - !p2 - !p4
.padded_box
+padded_column(!two_columns_secondary, 10px, 0, 5px, 15px)
:background #f0f0f0