Can I define a LESS mixin to generate a transition-property with a variable number of parameters?

Mark Whitaker picture Mark Whitaker · May 2, 2012 · Viewed 12.7k times · Source

I'm introducing LESS to a large web app project to simplify my CSS. I've got a few CSS rules which apply transitions to a varying number of properties, for example:

.movable {
    transition-property: top, left;
    transition-duration: 0.2s;
    transition-timing-function: ease;
}

.fadeAndStretchable {
    transition-property: opacity, width, height, margin;
    transition-duration: 1.5s;
    transition-timing-function: ease-out;
}

(Note: I've omitted -webkit, -moz and -o properties here for brevity: in reality each of these rules is 12 lines long rather than 3.)

Note that the values for transition-property are comma-separated. This is unusual in CSS: multiple values are usually space-separated (as in border: 1px solid #f00). LESS mixins can use the special @arguments value to produce a space-separated list of all the mixin arguments - but is it possible to define a LESS mixin that takes a variable number of parameters and turns them into a comma-separated value list, suitable for transition-property?

If necessary, I'm happy with a solution that requires two mixins: one for transition-property and another for transition-duration and transition-timing-function. Here's what I've tried so far:

Attempt 1: using @arguments with unnamed parameters

.transition-property() {
    -webkit-transition-property: @arguments;
    -moz-transition-property: @arguments;
    -o-transition-property: @arguments;
    transition-property: @arguments;
}

.movable {
    .transition-property(top, left);
}

Result: LESS error ("No matching definition was found for '.transition-property(top, left)'")

Attempt 2: using @arguments with named parameters

.transition-property(@p1, @p2, @p3, @p4, @p5) {
    -webkit-transition-property: @arguments;
    -moz-transition-property: @arguments;
    -o-transition-property: @arguments;
    transition-property: @arguments;
}

.movable {
    .transition-property(top, left);
}

Result: LESS error ("No matching definition was found for '.transition-property(top, left)'")

Attempt 3: using named parameters with dummy default values

.transition-property(@p1:p1, @p2:p2, @p3:p3, @p4:p4, @p5:p5) {
    -webkit-transition-property: @p1, @p2, @p3, @p4, @p5;
    -moz-transition-property:  @p1, @p2, @p3, @p4, @p5;
    -o-transition-property:  @p1, @p2, @p3, @p4, @p5;
    transition-property:  @p1, @p2, @p3, @p4, @p5;
}

.movable {
    .transition-property(top, left);
}

Result: No LESS error but it generates a CSS rule -webkit-transition-property: top, left, p3, p4, p5 that the browser ignores because of the unrecognised properties.

I've tried various other approaches (e.g. passing the property as a string 'top,left') but all result in the same thing: either a LESS error or invalid CSS.

Is there any way round this? Or do I have to bite the bullet and define a set of mixins overloaded on arity, e.g.

.transition-property(@p1) {...}
.transition-property(@p1, @p2) {...}
.transition-property(@p1, @p2, @p3) {...}
.transition-property(@p1, @p2, @p3, @p4) {...}
etc.

Answer

Mark Whitaker picture Mark Whitaker · May 2, 2012

I've managed to figure it out thanks to Luke Page pointing me towards the ... syntax.

The solution was to use the following:

Phew. Here's the resulting mixin:

.transition-properties(...) {
    -webkit-transition-property: ~`"@{arguments}".replace(/[\[\]]/g, '')`;
}

And here's the full version with a complete set of browser extensions:

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