LESS and the @arguments Variable

I’ve converted all of my CSS workflows into LESS over the past few months and haven’t looked back. By far one of the best features is parametric mixins, which, in its most common use, takes the pain out of making sure you’ve added all of those pesky browser prefixes for CSS3 properties.

The one annoyance I’ve been facing recently is the way the @arguments variable is concatenated with spaces as opposed to commas. This may make sense for most mixins, but there are a few CSS properties where concatenating all of the given arguments with commas makes much more sense, as there can be multiple values for each property—box-shadow and text-shadow for instance. Take a look at this Gist comment for an example of what happens.

There’s a relatively simple fix of just string escaping your declaration:

.box-shadow(~"1px 1px 2px rgba(0,0,0,0.5), inset -1px -1px 0 rgba(255,255,255,0.5)");

However, the nitpicky problem I have with that is: I either have to manually write out my color value or create multiple scoped variables and interpolate them into the string (e.g., ~"@{color1}"). What I prefer to do is this:

.box-shadow(1px 1px 2px fadeout(@black, 50%), inset -1px -1px 0 fadeout(@white, 50%));

This keeps things easier to read, especially if I were to use a more complex color, and makes less work for me as I don’t have to think about writing out the rgba() format.

The example above is what I wanted, so I found a nice little regex that I plopped into a few of my mixins that makes this happen exactly as I needed:

.box-shadow(@shadow, ...) {
  @props: ~`"@{arguments}".replace(/[\[\]]/g, '')`;
  -webkit-box-shadow: @props;
     -moz-box-shadow: @props;
          box-shadow: @props;
}

What this does is uses javascript to interpolate the arguments variable and wipes out all brackets, leaving only the commas. To demonstrate more visually, it’s doing this:

"[1,2,3]".replace(/[\[\]]/g, ''); // returns: "1,2,3"

In the end, I understand why it was decided to concat @arguments with spaces instead of commas, part of me just wishes there was a less-hacky way to specify the concatenation options either on a global or scoped level.

Update: 05/16/2012

I just realized when only passing one argument to the mixin (e.g., .box-shadow(1px 1px 2px fadeout(@black, 50%))), LESS builds the arguments variable using space-delimitation in the parameter block instead of commas, so the output looks like this:

box-shadow: 1px, 1px, 2px, rgba(0,0,0,0.5)

So, obviously that doesn’t work. Lame. In the interim I just have two mixins: one for a single property value, and one for multiple values:

.box-shadow(@shadow) {
  -webkit-box-shadow: @shadow;
     -moz-box-shadow: @shadow;
          box-shadow: @shadow;
}
.multi-box-shadow(...) {
  @props: ~`"@{arguments}".replace(/[\[\]]/g, '')`;
  -webkit-box-shadow: @props;
     -moz-box-shadow: @props;
          box-shadow: @props;
}
Update 5/31/12: The Solution!

Commenter Vicente created an interesting solution by supplying two arguments (followed by the ellipses), with the second argument having a default value so the @arguments array will always be interpreted correctly:

.box-shadow(@shadowA,@shadowB:transparent 0 0 0, ...) {
    @props: ~`"@{arguments}".replace(/[\[\]]/g, '')`;
    -moz-box-shadow:@props;
    -o-box-shadow:@props;
    -webkit-box-shadow:@props;
    box-shadow:@props;
}

The problem I had with this implementation was the mixin creating unnecessary code when passing only one shadow. The output would end up looking like this (shortened for brevity):

box-shadow: 1px, 1px, 2px, rgba(0,0,0,0.5), transparent 0 0 0;

In the end it’s a good idea, so I took it one step further to prevent generating any unnecessary code, finally solving my dilemma with these CSS properties: I used an “X” as the default for the second variable and added a little extra to my regex, which will filter out the X (and trailing comma) from the stringified array:

.box-shadow(@shadowA, @shadowB:X, ...){
    @props: ~`"@{arguments}".replace(/[\[\]]|\,\sX/g, '')`;
    -webkit-box-shadow: @props;
       -moz-box-shadow: @props;
         -o-box-shadow: @props;
            box-shadow: @props;
}

At last, you can finally add an infinite numbers of box shadows (or any other complex comma separated properties for that matter) via mixins!

Note: 06/25/13, This solution is no longer needed.

As commenter Matthew points out, as of the 1.3.2 release, this solution is no longer needed. LESS now has built-in support for comma-separated values, via semicolon (;) delimiters. An example is below:

.box-shadow(1px 1px 2px fadeout(@black, 50%); inset -1px -1px 0 fadeout(@white, 50%));

Tagged:

11 Responses to LESS and the @arguments Variable

  1. Vicente says:

    Thanks!

    I used with only one mixin:

    .box-shadow(@shadowA,@shadowB:transparent 0 0 0,(…)){
    @props: ~`”@{arguments}”.replace(/[\[\]]/g, ”)`;
    -moz-box-shadow:@props;
    -o-box-shadow:@props;
    -webkit-box-shadow:@props;
    box-shadow:@props;
    }

    • Tony Stuck says:

      That does work, though it sucks to have extra code when only having one box shadow (which is probably the most typical usage anyway). You did give me a great idea however, check above for the update :)

  2. Ricardo says:

    Hey there. I wrote that line at http://stackoverflow.com/questions/8503088/concatenating-arbitrary-number-of-values-in-lesscss-mixin/8515414#8515414 almost an year ago! Wasn’t expecting it to see it in the wild, IMO using a @var is still the simpler, clean way.

    cheers!

    • Tony Stuck says:

      Nice! I don’t think I saw your specific post/response in my search for answers but likely someone else who reposted your answer on another thread :) .. Otherwise I may have just been satisfied with using a @var as well—didn’t even think about that at the time!

  3. Pingback: JavaScript Regex Pattern that contains parentheses | Jisku.com - Developers Network

  4. Cory Webb says:

    This method doesn’t work when you are using the LESS PHP compiler. Any ideas for a workaround so that this will work with the PHP compiler?

    • Tony Stuck says:

      I imagine this is because the PHP compiler doesn’t have a JS interpreter (see: here), so the javascript evaluation to parse the @arguments array isn’t running. I’d try to find out if the PHP compiler has a way to evaluate a PHP expression in the LESS file and then you could convert the JS evaluation to an equivalent PHP one..

  5. Chris M. says:

    Thanks! That works like a charm. I knew there had to be a way to do this, while not the prettiest solution, it definitely solves my problem!

  6. Pingback: Handling Multiple CSS3 Transitions with a LESS Mixin | aLittle{Code}

  7. Matthew says:

    This approach is no longer needed with LESS. You can separate your arguments with semi-colons, allowing comma-separated values.

  8. Pingback: 有关less 处理@arguments的一些高级技巧 – 司徒正美 | 查问题

Leave a Reply

Your email address will not be published. Required fields are marked *