Search code examples
lessauto-generate

Auto-generate LESS styles for sprite icons


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?


Solution

  • 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.)