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"