I have the following mixin:
.transition (#property, #duration: 0.2s) {
-webkit-transition: #property #duration ease-in-out;
-moz-transition: #property #duration ease-in-out;
-ms-transition: #property #duration ease-in-out;
-o-transition: #property #duration ease-in-out;
transition: #property #duration ease-in-out;
}
How can I change the mixin to allow multiple properties (i.e. background and color transition)?
There a lot of similar Q&A here at SO already (for instance) but since most of those are full of really outdated (mis)information and hacks it would make sense to write a new answer I guess:
-
Well, you need to decide if you want the mixin to detect each property value as an individual argument (with optional default value) or just pass them all together (these are sort of incompatible requirements, it's still possible to combine them though - see later).
In the simplest case the mixin should be defined just as:
.transition(#values) {
transition: #values;
}
Nothing more than that, and used like this:
.transition(width);
.transition(color 0.2s ease-in-out);
.transition(opacity 2s ease-in, height 5s ease-out;);
// etc.
See the documetation about using comma separated list as mixin arguments.
-
Now about single values as individual mixin parameters and default options. (Honestly for me this tendency to blindly put some pretty random default value for every singe parameter of every singe mixin looks like a common anti-pattern but either way). As you may notice the above mixin definition does not let you to specify a default value for individual parameters.
Obviously if you define your mixin as:
.transition(#property, #duration: 0.2s, #timing: ease-in-out) {
transition: #arguments;
}
(or similar) it cannot handle multiple properties anymore (#duration and #timing won't match corresponding arguments no matter what syntax you use to call it (with just a few specific exceptions). E.g.
.transition(opacity 2s ease-in, height 5s ease-out;);
would result in
transition: opacity 2s ease-in, height 5s ease-out 0.2s ease-in-out;
etc. which does not make any sense)
So if you still need both (yet again not counting this often makes such mixin usage uncertain and confusing) you have to invent some way to handle both variants somehow. For instance the simplest method (just one of literally zillion possible variants) would be just to provide different definitions for different number of values in the first argument (see conditional mixins), e.g.:
.transition(#property, #duration: 0.2s, #timing: ease-out)
when (length(#property) = 1) {
transition: #arguments;
}
.transition(...) when (default()) {
transition: #arguments;
}
With usage being the same as above except .transition(width); (and similar stuff) now has different result.
-
And as always, regardless of all above if it's only about writing vendor-prefixing mixins (and I guess it is) - just stop doing that.
This ought to work:
.transition (#property; #duration: 0.2s) {
-webkit-transition-property: #property;
-moz-transition-property: #property;
-o-transition-property: #property;
transition-property: #property;
-webkit-transition-duration: #duration;
-moz-transition-duration: #duration;
-o-transition-duration: #duration;
transition-duration: #duration;
-webkit-timing-function: ease-in-out;
-moz-timing-function: ease-in-out;
-o-timing-function: ease-in-out;
timing-function: ease-in-out;
}
.test {
.transition(color, background; 3s)
}
The biggest trick here is using a semicolon as a delimiter for the mixin, so that we can use the comma as delimiter for the transition-property. Also, you don't need the -ms- prefix, IE never had it for transitions.
Why not like this?
Mixin:
.transition(#transition) {
-webkit-transition: #transition;
-o-transition: #transition;
transition: #transition;
}
and use it:
for example: .transition(~"color ease-in-out 1s, background-color ease-in .5s");
Hope it helps
Related
I have a multiple transition problem with scss #mixin.
Im trying to create dynamic transition #mixin with 1-5 different properties. When I'm processing the code below this error shows up:
Error: Mixin transition takes 1 argument but 3 were passed.
on line 758 of style.scss, in `transition'
from line 758 of style.scss Use --trace for backtrace.
This is my code:
#mixin:
#mixin transition($x){
transition: $x;
-webkit-transition: $x;
-moz-transition: $x;
-ms-transition: $x;
-o-transition: $x;
}
#include:
#include transition(visibility 0s ease 0.2s, opacity 0.2s ease, transform 0.3s ease);
I figured it out with this hack but it looks like a very unclean solution to me:
#include transition(visibility 0s ease 0.2s + "," + opacity 0.2s ease + "," + transform 0.3s ease);
Is there a better way to do it?
In your mixin, you have declared a single variable $x as a parameter which means that sass expects the mixin to be called with one argument.
#include transition(visibility 0s ease 0.2s)
When you pass the mixin comma separated values, it causes an error because sass sees these as multiple values instead of a single value which it expects.
#include transition(visibility 0s ease 0.2s, opacity 0.2s ease) //Sees two args instead of one arg
In Sass, comma separated values can be interpreted as a single value if declared as varargs. Varargs are mixin or function parameters declared with 3 dots appended to their name.
Replacing your $x parameter with $x... will ensure that sass interprets the comma separated arguments passed to your mixin as one value.
#mixin transition($x...){
-webkit-transition: $x;
-moz-transition: $x;
-ms-transition: $x;
-o-transition: $x;
transition: $x;
}
It can then be used like this
div {
#include transition(color 1s, background-color 1s, border-color 1s);
}
which compiles to
div {
-webkit-transition: color 1s, background-color 1s, border-color 1s;
-moz-transition: color 1s, background-color 1s, border-color 1s;
-ms-transition: color 1s, background-color 1s, border-color 1s;
-o-transition: color 1s, background-color 1s, border-color 1s;
transition: color 1s, background-color 1s, border-color 1s;
}
By doing this you can pass the values as you normally would in CSS without the hack you are currently using making it much cleaner.
Hope this helps
Since this is the first result on Google, I want to say that this does not solve my problem. I wanted to transition multiple properties, with only one mixin. I came up with this solution: (see link for helper functions)
/*
usage: #include transition(prop1, prop2, ..., 0.5s cubic-bezier(0.16, 0.85, 0.45, 1));
*/
#mixin transition($args...) {
$type: nth($args, length($args));
$props: remove-nth($args, length($args));
$result: ();
#for $i from 1 through length($props) {
$prop: nth($props, $i);
$result: append($result, $prop);
$result: append($result, $type);
#if $i != length($props) {
$result: append($result, unquote($string: ","));
}
}
#include simple_transition($result);
}
I created a short mixin that allows adding multiple transition properties in one declaration. In case number of arguments provided for the timing, easing or delay, is less than number of transition properties, the arguments are repeated.
#mixin transition($prop, $time, $easing: $ease1, $delay: 0s) {
$transition: ();
#for $i from 1 through length($prop) {
#for $j from 0 to (length($prop)) - (length($time)) {
$time: join($time, nth($time, -1));
}
#for $j from 0 to (length($prop)) - (length($easing)) {
$easing: join($easing, nth($easing, -1));
}
#for $j from 0 to (length($prop)) - (length($delay)) {
$delay: join($delay, nth($delay, -1));
}
$transition: append(
$transition,
(nth($prop, $i) nth($time, $i) nth($easing, $i) nth($delay, $i)),
$separator: comma
);
}
transition: $transition;
}
//scss input:
#include transition(height width transform, 0.2s 0.3s, linear, 0s);
//css output:
transition: height 0.2s linear 0s, width 0.3s linear 0s, transform 0.3s linear 0s;
Where time and easing are the same, but with multiple properties:
#mixin transitionPrefixMultiple($time, $properties...) {
$transition: ();
#each $property in $properties {
$transition: append(
$transition, ($property $time cubic-bezier(.42, 0, .58, 1)), $separator: comma
);
}
-webkit-transition: $transition;
-moz-transition: $transition;
-ms-transition: $transition;
-o-transition: $transition;
transition: $transition;
}
Usage:
#include transitionPrefixMultiple(150ms, width, background-color, etc);
Thank you #nidhishs06 as this is a cleaner version of your answer
I'm seeing if there is a way to blur out your background for a few seconds then have it come into view. I was trying to play around with the blur attribute and the transition attribute but I was not getting the results I was getting. I tried something like this on my css
body{
background:black;
-webkit-filter: blur(10px);
}
.body{
margin:0 auto;
width:95%;`enter code here`
clear:both;
transition:body 2s ease-in-out;
}
Any suggestions?
Presumably you don't have a class named "body" so the period in your second selector is an error. Also, you have the syntax wrong for transition. The first value is the property to transition, not a selector. And, depending on the level of support you require, you need vendor prefixes on both the transition property and the property being transitioned, e.g.
body {
-webkit-transition: -webkit-filter 2s ease-in-out;
}
BTW, I'm not sure if -filter is an animatable property.
Fiddle: http://jsfiddle.net/Bushwazi/sHL9m/3/
1) Use all in the transition
2) Despite what the prefixes lead you to believe, this didn't work in all browsers
3) But it did work the same for backgrounds and img
I change a class using javascript, css handles the rest.
.filter {
filter:blur(10px);
-webkit-filter:blur(10px);
-moz-filter:blur(10px);
-o-filter:blur(10px);
-ms-filter:blur(10px);
-webkit-transition: all 5.0s linear 1.0s;
-moz-transition: all 5.0s linear 1.0s;
-ms-transition: all 5.0s linear 1.0s;
-o-transition: all 5.0s linear 1.0s;
transition: all 5.0s linear 1.0s;
}
.filter.animae {
filter:blur(0px);
-webkit-filter:blur(0px);
-moz-filter:blur(0px);
-o-filter:blur(0px);
-ms-filter:blur(0px);
}
.bg {
display:inline-block;
width:360px;
height:424px;
background:url(http://gruntjs.com/img/grunt-logo.svg);
}
LESS allows parametric mixins, such as:
.transition(#property, #duration){
transition: #property #duration;
-moz-transition: #property #duration; /* Firefox 4 */
-webkit-transition: #property #duration; /* Safari and Chrome */
-o-transition: #property #duration; /* Opera */
}
However, this doesn't always work with properties such as transitions. If you are trying to have multiple transitions and attempt to call the mixin multiple times, the last mixin overrides all previously defined transitions. That's because the proper CSS3 syntax for defining multiple transitions is:
... {
transition: #property1 #duration1, #property2 #duration2, ...;
}
The only way that I can think of to define multiple transitions as mixins is to overload the mixin:
.transition(#property, #duration){...}
.transition(#property, #duration, #prop2, #dur2){...}
.transition(#property, #duration, #prop2, #dur2, #prop3, #dur3){...}
Is there a more robust and concise way of defining the transition mixin to take in a variable number of arguments and construct the appropriate transition CSS?
Context: Sometimes I'd like to transition on multiple properties; for example, a :hover might trigger transitions on background color, box-shadow, text-color, etc...
See my answer here: Multiple properties are getting treated as separate arguments in mixins
Summary: use this mixin for variable number of arguments:
.transition (#value1,#value2:X,...)
{
#value: ~`"#{arguments}".replace(/[\[\]]|\,\sX/g, '')`;
-webkit-transition: #value;
-moz-transition: #value;
-ms-transition: #value;
-o-transition: #value;
transition: #value;
}
UPDATE for LESS 1.3.3+
Output is the same, but note the difference in how the properties can be passed in the newer versions of LESS by using the semicolon instead of doing an escaped string:
#prop1: color;
#prop2: opacity;
#dur1: 3s;
#dur2: 4s;
.transition(#transString: 0) when not (#transString = 0) {
transition: #transString;
-moz-transition: #transString; /* Firefox 4 */
-webkit-transition: #transString; /* Safari and Chrome */
-o-transition: #transString; /* Opera */
}
.class1 {.transition();}
.class2 {.transition(width 2s, height 2s;);}
^
semicolon here
.class3 {.transition(#prop1 #dur1, #prop2 #dur2;);}
^
semicolon here
The semicolon forces the commas to be evaluated as list separators rather than parameter separators.
One Solution for LESS pre 1.3.3
We build the correct property arguments as a string for the transition, then use the escaped value (~) operator to translate that into the proprietary syntax needed. By using string interpolation (#{variableName}) we can even embed variables into the process, but the actual input needs to be in the form of an escaped string.
LESS Code
#prop1: color;
#prop2: opacity;
#dur1: 3s;
#dur2: 4s;
.transition(#transString: 0) when not (#transString = 0) {
transition: #transString;
-moz-transition: #transString; /* Firefox 4 */
-webkit-transition: #transString; /* Safari and Chrome */
-o-transition: #transString; /* Opera */
}
.class1 {.transition();}
.class2 {.transition(~" width 2s, height 2s");}
.class3 {.transition(~" #{prop1} #{dur1}, #{prop2} #{dur2}");}
CSS Output
Note: no .class1 is output because the guard expression insures that something is input (though it does not guard against improper input).
.class2 {
transition: width 2s, height 2s;
-moz-transition: width 2s, height 2s;
-webkit-transition: width 2s, height 2s;
-o-transition: width 2s, height 2s;
}
.class3 {
transition: color 3s, opacity 4s;
-moz-transition: color 3s, opacity 4s;
-webkit-transition: color 3s, opacity 4s;
-o-transition: color 3s, opacity 4s;
}
In LESS, you can separate arguments using commas OR semi-colons. For single values that include commas, you can terminate that single value with a semi-colon in order to send the list as a single value, like this:
.class {
.background-size(100%, auto;);
}
For multiple values, just use this syntax:
/* Example mixin */
.set-font-properties(#font-family, #size) {
font-family: #font-family;
font-size: #size;
}
/* Usage with comma-separated values */
.class {
.set-font-properties(Arial, sans-serif; 16px);
}
/* Output */
.class {
font-family: Arial, sans-serif;
font-size: 16px;
}
Easy peasy!
Note: This answer is not added with the intention of saying the existing answers are incorrect or obsolete. All the answers are valid and would still work. This one just provides a different method which in my opinion is a bit more complex but also more flexible in terms of how each argument can be mentioned as key-value pairs.
Advantages of using this method: This method would become more useful when there is a need to perform any extra operation on the values (say like adding unit as deg, px or performing any extra math operations etc) or dynamically adding the vendor prefixes for the #property also. For example there are times when you might want to pass only transform as an input property to the mixin but want to add -webkit-transform for the -webkit-transition and -moz-transform for the -moz-transition etc.
In this method, we make use of the ... feature which allows us to pass variable number of arguments to a mixin, loop over each argument that is passed, extract the name of the property along with the additional parameters (like duration, degree of rotation etc) and then use the merge feature that is provided by Less to concatenate the values specified for the property.
The +: concatenates the property values with a comma and was introduced in Less v1.5.0
The +_: concatenates the property values with a space and was introduced in Less v1.7.0.
.transition(#args...){
.loop-args(#argCount) when (#argCount > 0) {
.loop-args(#argCount - 1);
#arg: extract(#args, #argCount);
#property: extract(#arg,1);
#duration: extract(#arg,2);
-webkit-transition+: #property #duration;
-moz-transition+: #property #duration;
-o-transition+: #property #duration;
transition+: #property #duration;
}
.loop-args(length(#args));
}
div{
.transition(background, 1s; border-color, 2s; color, 2s);
}
.transform(#args...){
.loop-args(#argCount) when (#argCount > 0) {
.loop-args(#argCount - 1);
#arg: extract(#args, #argCount);
#property: extract(#arg,1);
#param: extract(#arg,2);
-webkit-transform+_: ~"#{property}(#{param})";
-moz-transform+_: ~"#{property}(#{param})";
-o-transform+_: ~"#{property}(#{param})";
transform+_: ~"#{property}(#{param})";
}
.loop-args(length(#args));
}
div#div2{
.transform(rotate, 20deg; scale, 1.5; translateX, 10px);
}
The above code when compiled would produce the below output:
div {
-webkit-transition: background 1s, border-color 2s, color 2s;
-moz-transition: background 1s, border-color 2s, color 2s;
-o-transition: background 1s, border-color 2s, color 2s;
transition: background 1s, border-color 2s, color 2s;
}
div#div2 {
-webkit-transform: rotate(20deg) scale(1.5) translateX(10px);
-moz-transform: rotate(20deg) scale(1.5) translateX(10px);
-o-transform: rotate(20deg) scale(1.5) translateX(10px);
transform: rotate(20deg) scale(1.5) translateX(10px);
}
Related Answer:
Here is an answer from seven-phases-max which explains more on how this method could be used to auto add vendor prefixes like I have mentioned in the advantages paragraph.
This should work, I think:
.transition(...) {
transition: #arguments;
-moz-transition: #arguments; /* Firefox 4 */
-webkit-transition: #arguments; /* Safari and Chrome */
-o-transition: #arguments; /* Opera */
}
... - is a valid less syntax, not something to be replaced.
Current as of LESS 1.4, the documentation (http://lesscss.org/features/#mixins-parametric-feature-mixins-with-multiple-parameters) suggests the proper way to handle this:
Using comma as mixin separator makes it impossible to create comma
separated lists as an argument. On the other hand, if the compiler
sees at least one semicolon inside mixin call or declaration, it
assumes that arguments are separated by semicolons and all commas
belong to css lists:
Concretely, mixin:
.transition(#prop-or-props) {
-webkit-transition: #prop-or-props;
-moz-transition: #prop-or-props;
-o-transition: #prop-or-props;
transition: #prop-or-props;
}
usage:
.transition(opacity .2s, transform .3s, -webkit-transform .3s;);
Note that multiple properties are separated by commas and the trailing semi-colon causes the comma separated list to be treated as a single parameter in the mixin.
It would be nicer to define the mixin with a rest... parameter and be able to extract each element of the arbitrary-length arguments for separate handling, but the use-case I'm thinking of is adding vendor prefixes to transform transitions (so i could call it simply with .transition(opacity .2s, transform .3s) and have the -webkit-transform bit added automatically) and perhaps this is better handled by a different utility anyway (gulp-autoprefixer, for example).
I currently have -webkit specific attributes in my Less CSS sheet, I am trying to update them with mixins to add -moz attributes, like this:
.transition(#1) {
-webkit-transition: #1;
-moz-transition: #1;
}
div {
.transition(all .5s);
}
The example above works fine, however I also have things like that:
div {
-webkit-transition: border-color .3s, background .3s;
}
And I can’t call the mixin as .transition(border-color .3s, background .3s) because it has more arguments than defined in the mixin. So what I am doing at the moment is this:
.transition(#1) {
-webkit-transition: #1;
-moz-transition: #1;
}
.transition-2(#1, #2) {
-webkit-transition: #1, #2;
-moz-transition: #1, #2;
}
div {
.transition-2(border-color .3s, background .3s);
}
This is annoying, I need to add redundant code in my sheet any time I’m using a number of arguments not previously used before; and I have this problem with others CSS3 properties too, for example box-shadow when I need to add inset at the beginning.
Is there any way to declare mixins flexible in their number of arguments with Less, just like CSS3 properties are?
For this case, the redundant mixin code can be avoided using any one of the below mentioned options.
Option 1: (Simplest solution - thanks to seven-phases-max for highlighting the miss)
We can use semi-colon as a separator/delimiter for arguments and when we add a semi-colon at the end after specifying all properties that need to be transitioned (in a comma separated format), the whole part before it would be considered as one single argument.
Extract from the official Less website:
Using comma as mixin separator makes it impossible to create comma separated lists as an argument. On the other hand, if the compiler sees at least one semicolon inside mixin call or declaration, it assumes that arguments are separated by semicolons and all commas belong to css lists
.transition(#1) {
-webkit-transition: #1;
-moz-transition: #1;
}
div{
.transition(border-color .5s, background .3s, color .3s;);
}
So the above code when compiled would result in
div {
-webkit-transition: border-color 0.5s, background 0.3s, color 0.3s;
-moz-transition: border-color 0.5s, background 0.3s, color 0.3s;
}
Option 2:
Pass the input values to the mixin (how many ever specific properties need to be transitioned) within quotes. Within the mixin, use the ~ or the e() in-built functions to strip the quotes.
.transition(#1) {
-webkit-transition: ~"#{1}";
-moz-transition: ~"#{1}";
}
div {
.transition("border-color .5s, background .3s");
}
div#sample2 {
.transition("border-color .3s, background .3s, color .3s");
}
will produce the below CSS when compiled
div {
-webkit-transition: border-color .5s, background .3s;
-moz-transition: border-color .5s, background .3s;
}
div#sample2 {
-webkit-transition: border-color .3s, background .3s, color .3s;
-moz-transition: border-color .3s, background .3s, color .3s;
}
Option 3:
Less does allow creation of mixins which allow/accept variable number of inputs using the ... option. Hence you can use the same mixin as in your original code by adding the ... to the input variable and calling it as you had originally wanted.
.transition(#args...) {
-webkit-transition: #args;
-moz-transition: #args;
}
div {
.transition(border-color .5s, background .3s);
}
The above will compile successfully but the only problem is that it would produce the below output when compiled. As you can see, the problem is that the parameter values are space separated and not comma separated (as they should be for the CSS to work properly).
div {
-webkit-transition: border-color 0.5s background 0.3s;
-moz-transition: border-color 0.5s background 0.3s;
}
Ofcourse we could write complex replace functions using regular expressions but that would really make the code messy. Instead we could use loops and some built-in functions to achieve the required output (like shown below).
.transition(#args...) {
.loop-args(#argCount) when (#argCount > 0) {
.loop-args(#argCount - 1);
#arg: extract(#args, #argCount);
-webkit-transition+: #arg;
-moz-transition+: #arg;
}
.loop-args(length(#args));
}
div {
.transition(border-color .5s, background .3s, color .3s);
}
Basically what we are doing is use the ... to accept multiple arguments as input to the mixin and then loop over each argument and add it to the CSS property's value. The +: (merge function introduced in Less v1.5.0) is used to produce the comma separated output.
When compiled, it would produce the below output:
div {
-webkit-transition: border-color 0.5s, background 0.3s, color 0.3s;
-moz-transition: border-color 0.5s, background 0.3s, color 0.3s;
}
You could try
.transition(#1) {
-webkit-transition: #1;
-moz-transition: #1;
}
.transition-2(#1, #2) {
.transition(#1); // this includes all the stuff from transition(#1)
color:red; // additional stuff
}
As for your actual question, I dont believe that LESS itself has any sort of "rest" style arguments passing.
I'm just converting a alredy made CSS file into a LESS and would like to know why cant I add transition-timing function in the less stylesheet
the code
-webkit-transition-timing function: linear, ease-in;
-moz-transition-timing function: linear, ease-in;
transition-timing function: linear, ease-in;
the error
the reason?
1) You're missing the - before function.
2) You can only define one value for *-transition-timing-function.
CSS
transition-timing-function: ease-in;
-o-transition-timing-function: ease-in;
-moz-transition-timing-function: ease-in;
-webkit-transition-timing-function: ease-in;