LESS: How can I pass a mixin as an argument to another mixin?
In this particular case (unlike a general case with an arbitrary mixin name) I'd say you're missing the fact that in .on-small
/.on-medium
these small
and medium
things are also nothing but parameters and thus should not be a part of the mixin names. With this in mind your example becomes:
.on(small, @rules) {
@media (@minWidthSmall) {@rules();}
}
.on(medium, @rules) {
@media (@minWidthMedium) {@rules();}
}
.make-col(@device, @span, @size) {
flex: 1;
box-sizing: border-box;
.on(@device, {
width: percentage(@span/@size);
min-width: percentage(@span/@size);
});
}
// usage:
.make-col(small, @span, @size);
Same for your .grid-col-on-*
mixins, they are just a single:
.grid-col-on(@device, @span: 1, @size: 1) {
.make-col(@device, @span, @size);
}
and so on.
If you really want a flexible/generic grid - never hardcode device/breakpoint names into mixin or variable names (for more rationale and examples see https://github.com/less/less.js/issues/2702).
LESS CSS Pass mixin as a parameter to another mixin
UPDATED for LESS 1.7.0+ (WAY Simpler)
We can do this far more directly now with the 1.7.0 update and the ability to create rulesets, and to use variables in setting @keyframes
.
Now we really can pass a mixin through a parameter by a ruleset, or we can pass in the property stings themselves. So consider this:
LESS (using 1.7)
.keyframes(@name, @from, @to) {
@frames: {
from { @from(); }
to { @to(); }
};
@pre: -moz-keyframes;
@-moz-keyframes @name
{
@frames();
}
@-webkit-keyframes @name
{
@frames();
}
@keyframes @name
{
@frames();
}
}
.keyframes(testName, {color: red; .myMix(0);}, {color: blue; .myMix(1);});
.myMix(@value) {opacity: @value;}
Note that I am passing both a property setting and a mixin call, and my output is:
CSS Output
@-moz-keyframes testName {
from {
color: red;
opacity: 0;
}
to {
color: blue;
opacity: 1;
}
}
@-webkit-keyframes testName {
from {
color: red;
opacity: 0;
}
to {
color: blue;
opacity: 1;
}
}
@keyframes testName {
from {
color: red;
opacity: 0;
}
to {
color: blue;
opacity: 1;
}
}
Note how the rulesets are passed, in brackets {...}
, and then called, via @from()
and @to()
(looking a lot like a mixin call). I'm using these passed rule sets to set another ruleset of @frames
which is then itself called to fill the keyframes definitions.
More Generically
Here I pass a private mixin to another mixin and then call it from that other mixin:
LESS
.someMixin(@class; @expectedMixin) {
.@{class} {
@expectedMixin();
.myPrivateMix(0.6);
test: 1;
}
}
.someMixin(newClass; {.myClass;});
.myClass {
.myPrivateMix(@value) {opacity: @value;}
}
CSS Output
.newClass {
opacity: 0.6;
test: 1;
}
Kept the below for legacy info.
Updated (added LESS 1.4.0+ support)
Wow, this took some doing, but I think I have something you can work with. However, it does take some special defining of your mixins in your modules, specifically, using pattern matching. So...
First, Define Your Module Mixins
Note how the module mixins intended to be used in a specific future mixin are defined with the same mixin name, but with a different pattern name. This was key to making this work.
// define one animation in a module
.from(my-from){ color: red; }
.to(my-to) { color: blue; }
// define one animation in another module
.from(another-from){ font-size: 1em; }
.to(another-to) { font-size: 2em; }
If you also want individual mixin names in the modules, you should be able to do this:
// define one animation in a module
.my-from(){ color: red; }
.my-to() { color: blue; }
.from(my-from){ .my-from() }
.to(my-to) { .my-to() }
// define one animation in another module
.another-from(){ font-size: 1em; }
.another-to() { font-size: 2em; }
.from(another-from){ .another-from() }
.to(another-to) { .another-to() }
This should allow one to call either the straight mixin .my-from()
or, to make it variably accessible within later mixins that access the singular .from()
mixin group through the pattern matching.
Next, Define Your Mixin
For your @keyframes
example, that was extremely difficult. In fact, a stack overflow answer was vital to helping me solve an issue with applying the @name
, which was not applying under normal LESS rules because of it following the @keyframes
definition. The solution to apply the @name
looks nasty, but it works. It does have the, perhaps, unfortunate necessity of also defining a selector string to play the animation by (because it uses that string to help build the last }
of the keyframes). This naming limitation would only be true of css strings that begin with @
like @keyframes
and probably @media
.
Further, because we have a standard mixin name used in our module files, we can access that consistently within our new mixin, while at the same time passing a variable in to select the proper variation of that mixin through a pattern match. So we get:
LESS 1.3.3 or under
// define mixin in mixin file
.keyframes(@selector, @name, @from, @to) {
@newline: `"\n"`; // Newline
.setVendor(@pre, @post, @vendor) {
(~"@{pre}@@{vendor}keyframes @{name} {@{newline}from") {
.from(@from);
}
to {
.to(@to);
}
.Local(){}
.Local() when (@post=1) {
(~"}@{newline}@{selector}") {
-moz-animation: @name;
-webkit-animation: @name;
-o-animation: @name;
-ms-animation: @name;
animation: @name;
}
}
.Local;
}
.setVendor("" , 0, "-moz-");
.setVendor(~"}@{newline}", 0, "-webkit-");
.setVendor(~"}@{newline}", 0, "-o-");
.setVendor(~"}@{newline}", 0, "-ms-");
.setVendor(~"}@{newline}", 1, "");
}
LESS 1.4.0+
.keyframes(@selector, @name, @from, @to) {
@newline: `"\n"`; // Newline
.setVendor(@pre, @post, @vendor) {
@frames: ~"@{pre}@@{vendor}keyframes @{name} {@{newline}from";
@{frames} {
.from(@from);
}
to {
.to(@to);
}
.Local(){}
.Local() when (@post=1) {
@animationSector: ~"}@{newline}@{selector}";
@{animationSector} {
-moz-animation: @name;
-webkit-animation: @name;
-o-animation: @name;
-ms-animation: @name;
animation: @name;
}
}
.Local;
}
.setVendor("" , 0, "-moz-");
.setVendor(~"}@{newline}", 0, "-webkit-");
.setVendor(~"}@{newline}", 0, "-o-");
.setVendor(~"}@{newline}", 0, "-ms-");
.setVendor(~"}@{newline}", 1, "");
}
Now Call Your Mixin
You can give it your own name, and just pass the straight pattern (all are no dot [.] and no quotes) for the pattern matches on the module mixins, but don't forget that you also need a selector string (which is quoted) to get the mixin to work right:
.keyframes('.changeColor', some-name, my-from, my-to);
.keyframes('.changeFontSize', another-name, another-from, another-to);
Which Gives You the Desired Output
@-moz-keyframes some-name {
from {
color: red;
}
to {
color: blue;
}
}
@-webkit-keyframes some-name {
from {
color: red;
}
to {
color: blue;
}
}
@-o-keyframes some-name {
from {
color: red;
}
to {
color: blue;
}
}
@-ms-keyframes some-name {
from {
color: red;
}
to {
color: blue;
}
}
@keyframes some-name {
from {
color: red;
}
to {
color: blue;
}
}
.changeColor {
-moz-animation: some-name;
-webkit-animation: some-name;
-o-animation: some-name;
-ms-animation: some-name;
animation: some-name;
}
@-moz-keyframes another-name {
from {
font-size: 1em;
}
to {
font-size: 2em;
}
}
@-webkit-keyframes another-name {
from {
font-size: 1em;
}
to {
font-size: 2em;
}
}
@-o-keyframes another-name {
from {
font-size: 1em;
}
to {
font-size: 2em;
}
}
@-ms-keyframes another-name {
from {
font-size: 1em;
}
to {
font-size: 2em;
}
}
@keyframes another-name {
from {
font-size: 1em;
}
to {
font-size: 2em;
}
}
.changeFontSize {
-moz-animation: another-name
-webkit-animation: another-name;
-o-animation: another-name;
-ms-animation: another-name;
animation: another-name;
}
Less CSS: Can I call a mixin as an argument when calling another mixin?
As discussed in comments, Less mixins are not functions and the mixin calls cannot return any value. Because of this, one mixin (or its output value) cannot be passed as an argument to another mixin.
Having said that, we can still set a variable within a mixin, call the mixin within each selector block where it is required and make use of the variable defined within it. The mixin call effectively exposes the variable defined within it to the parent scope.
Below is a sample snippet which would call the contrast mixin and assign the calculated value as the text color and border color of the element.
// color variables for user's color
@userColor: #13acae;
@darkUser: hsl(hue(@userColor), saturation(@userColor), lightness(tint(@userColor, 30%)));
@lightUser: hsl(hue(@userColor), saturation(@userColor), lightness(shade(@userColor, 30%)));
// color mixin to alter user's color using Less 'darken' and 'contrast' functions
.contrastColorDark(@percent) {
@color: darken(contrast(@userColor, @darkUser, @lightUser), @percent);
//color: darken(contrast(@userColor, @darkUser, @lightUser), @percent);
}
// border mixin
.border(@width, @color) {
border: @width solid @color;
}
// CSS rule using both mixins
.thing {
.contrastColorDark(10%);
color: @color;
.border(1px, @color);
}
.thing2 {
.contrastColorDark(50%);
color: @color;
.border(1px, @color);
}
pass property name as an argument in a mixin LESS
I think this is what you are trying to do:
//A simple Border Mixin to start
.borderMixin(@color: #ddd){
border: 1px solid @color;
}
//Using the Mixin
.border{
.borderMixin(@color: #ddd);
&-right {
.borderMixin(@color: #F01);
}
&-left {
.borderMixin(@color: #000);
}
}
So first I've declared the Mixin helper to be used and then I used it and re-use it exending the class name with the &
character
That will output this in your CSS:
/*********
*The resulted css code:
*/
.border {
border: 1px solid #dddddd;
}
.border-right {
border: 1px solid #ff0011;
}
.border-left {
border: 1px solid #000000;
}
UPDATE:
+Harry suggests this:
.borderMixin(@position: left, @color: #ddd){
border-@{position}: 1px solid @color;
}
.border{
width: 200px;
.borderMixin(right,#222);
.borderMixin(left,#222);
}
Mixin property as an argument of another mixin?
OK, so first of all, a general remark: using a CSS preprocessor (e.g. LESS, SASS or whatever) to generate vendor prefixes is actually one of the greatests misuses these days (really, there's no need to bloat your code with prefixes and waste your time writing such mixins since tools like Autoprefixer, -prefix-free and similar came in).
Either way here's a (sort of) generic solution (but considering amount of code and its complexity I think it's actually an overkill, here I will use LESS 1.6.0 example because in earlier versions it would be even more verbose and hackish):
// usage:
element1 {
.vendorize(transition-property; opacity, transform);
}
element2 {
.vendorize(transition-property; width, box-shadow, color);
}
element3 {
.vendorize(transition-property; height);
}
// implementation:
// prefixes we want to be used:
@prefixes: -webkit-, -moz-, -o-, ~'';
// here we specialize what values are to be prefixed:
.vendorize-value(transform) {.true}
.vendorize-value(box-shadow) {.true}
// etc.
.vendorize-value(...) when (default()) {.false} // to handle not prefixed values
// and now the mixin that can apply all of above specializations:
.vendorize(@property, @values) {
.-1();
.-1(@i: length(@prefixes)) when (@i > 0) {
.-1((@i - 1));
@prefix: extract(@prefixes, @i);
.-2();
}
.-2(@j: length(@values)) when (@j > 0) {
.-2((@j - 1));
@value: extract(@values, @j);
.vendorize-value(@value);
}
.false() {@{prefix}@{property}+: @value}
.true() {@{prefix}@{property}+: ~'@{prefix}@{value}'}
}
(Of course it can be simplified a bit if you need only transform
to be prefixed but still looks too crazy).
upd: fixed some errors.
How to pass a property name as an argument to a mixin in less
This is currently a feature request on less.js github. So look out for it in less.js 1.4.. until then you can hack it like so...
.mixin(@prop, @value) {
Ignore: ~"a;@{prop}:@{value}";
}
Not very nice and you get an extra property but its the only way at the moment.
Use mixin argument to create class name in LESS
I think I have the solution using Variable Names.
Less
@green: #5FBEAA;
.text-color(@colorname) {
@color: ~"@{colorname}";
.text-@{color}{
color: @@color;
}
}
.text-color(green);
Output
.text-green {
color: #5FBEAA;
}
LESS: Mixin with a unique argument to manage css3 transitions
You can use string escaping and interpolation like this:
.links-transition (@arg:"color 1s, border-color 1s") {
@color-time: ~"@{arg}";
-webkit-transition:@color-time;
-moz-transition:@color-time;
-ms-transition:@color-time;
-o-transition:@color-time;
transition:@color-time;
}
a {
color:red;
.links-transition ("color 2s, border-color 2s");
}
it will return this CSS:
a {
color: red;
-webkit-transition: color 2s, border-color 2s;
-moz-transition: color 2s, border-color 2s;
-ms-transition: color 2s, border-color 2s;
-o-transition: color 2s, border-color 2s;
transition: color 2s, border-color 2s;
}
hope this does what you want.
For more ideas: there are some additinal approaches/solutions that you can find on SO, like this two for example:
Less CSS: Mixins with Variable Number of Arguments
Multiple properties are getting treated as separate arguments in mixins
Update: in LESS 1.4 beta it works the way you wanted to do it:
.links-transition (@color-time: color 1s, border-color 1s;) {
-webkit-transition:@color-time;
-moz-transition:@color-time;
-ms-transition:@color-time;
-o-transition:@color-time;
transition:@color-time;
}
a {
color:red;
.links-transition (color 2s, border-color 2s;);
}
has the same output as the above solution. Comma separated argunets are possible since 1.3.2, however they can apparently not include whitespaces.
Related Topics
Bootstrap Ie Background Color Issue
Computed Width with Decimal Values in Firefox, But Without Decimals in Webkit
How to Overflow the Contents of a Column into the Next Column Using CSS
Why Do Modern Browsers Still Put Spaces Between Inline Block If There Is Whitespace
CSS - Fonts Render Differently in Firefox and Chrome
Css: How to Combine Multiple Stylesheets into One
Twitter Bootstrap Custom Carousel Indicators
Bootstrap 3 - Add an Asterisk to the Input on the Same Row
Blinking Loading Text in R Shiny
Is There a CSS Selector for an Img Which Has Been Constrained by Max-Width or Max-Height
Less: Can You Group a CSS Selector with a Media Query
CSS Negative Margins for Positioning
What Is the Recommended Way to Dynamically Set Background Image in Angular 4
Simulate "Inner Border" in CSS
Margin to Apply to Position Div After Skewing with CSS Transform