Mixin Property as an Argument of Another Mixin

How to pass a mixin as a parameter of another mixin in SASS

You can't add two mixins together the way you'd like. So you just have to make the rem mixin do what you want it to do. So I wrote new code to handle that for you.

@function parseInt($n) {
@return $n / ($n * 0 + 1);
}

@mixin rem($property, $values, $prefix: false) {
$px: ();
$rem: ();

@each $value in $values {

@if $value == 0 or $value == auto or unit($value) == "%" {
$px: append($px, $value);
$rem: append($rem, $value);
} @else {
$unit: unit($value);
$val: parseInt($value);

@if $unit == "px" {
$px: append($px, $value);
$rem: append($rem, ($val / 16 + rem));
}

@if $unit == "rem" {
$px: append($px, ($val * 16 + px));
$rem: append($rem, $value);
}
}
}

@if $px == $rem {
#{$property}: $px;
} @else if $prefix == true {
#{-moz- + $property}: $px;
#{-moz- +$property}: $rem;
#{-webkit- +$property}: $px;
#{-webkit- +$property}: $rem;
#{$property}: $px;
#{$property}: $rem;
} @else {
#{$property}: $px;
#{$property}: $rem;
}
}

Now all you have to do add prefixes to any property is add the value true to the end of the mixin like so...

@include rem(border-radius, 10px, true);

Otherwise if you don't want any prefixs on property like fon-size or something you just don't add a last value like so...

@include rem(font-size, 10px);

I have a working demo here...

*Also on a side note I modified this mixin to handle percentages too.

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.

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);
}

Send properties as argument for mixin

The LESS documentation about mixins says: "Mixins allow you to embed all the properties of a class into another class by simply including the class name as one of its properties. It’s just like variables, but for whole classes. Mixins can also behave like functions, and take arguments, as seen in the example bellow."

You can't have anything but css properties and their values, either stated or represented by a variable, in a mixin. So what you're trying to do is invalid.

Include a SASS Mixin via another Mixin Parameter Value

Seeing your markup above, I'm assuming you've tried interpolation already and it din't work. Just a thought - wouldn't an extend be more appropriate in this context? i.e. extend individual button-* mixins with a certain roundedness.

That being said, you already have the button $color parametrized, but you are taking a step back by adding an extra level of complexity of having a pre-defined mixin included depending on the variable value! I'm assuming that you've started with button-blue...button-tree mixins, and then a requirement came up needing a $circle toggle?

If you are, in fact, using button-blue...button-tree outside of the button mixin, I'd say it would be most maintainable to have the "multiple ifs" switch logic abstracted once to a function directive like

@function include-button-mixin($color) {
@if $color == blue
@include button-blue
...
}

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.

How to pass multiple args to a Sass Mixin: a list and named variable

You must put the arbitrary arguments at the end of the mixin's arguments list (See the doc)

Try this instead: It uses named arguments in the @include arguments list and options arguments (doc) in the @mixin definition

@mixin fast-transition($ieTransition: false, $properties: "all") {
transition-duration: 0.5s;
transition-timing-function: cubic-bezier(0.42, 0, 0.58, 1);
transition-property: $properties;

@if not($ieTransition) {
:global(.browser-ie) & {
transition: none;
}
}
}

to use it do this

.el-all {
// use all for transition properties and no transition for IE
@include fast-transition();
)
// or
.el-no-ie {
// use $properties for transition properties and no transition for IE
@include fast-transition($properties: 'background-color, color");
)
// or
.el-ie {
// use $properties for transition properties and transition for IE
@include fast-transition($properties: 'background-color, color", $ieTransition: true);
)

See this demonstration https://codepen.io/HerrSerker/pen/dyXEvWK?editors=1100

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;
}

TypeScript mixin constrained to another mixin

Yes, it is possible. Define your other mixin so that it's backed by an interface. For example:

interface Named {
name: string;
}

function Named<T extends Constructor<{}>>(Base: T) {
return class extends Base implements Named {
name: string;
};
}

You can then use the interface in an intersection type in the Constructor type argument:

const WithLocation = <T extends Constructor<Point & Named>>(Base: T) =>
class extends Base {
getLocation(): [number, number] {
console.log(this.name); // compiles
return [this.x, this.y];
}
};

const NamedPointWithLocation = WithLocation(Named(Point));
const PointerWithLocation = WithLocation(Point); // error, expects argument to be Named

You can see a more complicated example of this being done here that will work when compiling with declaration files.

Pass multiple arguments to single mixin argument

The best thing to do in this case would be to use a map to hold the property/value pairs, like:

(font-weight: 600, text-transform: uppercase)

Then you could add a parameter to hold the map in the mixin:

@mixin fonts($font-family, $primary-color, $styles){

And loop through, the map, inserting the styles into the rule:

@each $property, $value in $styles {
#{$property}: $value;
}

To put it all together:

@mixin fonts($font-family, $primary-color, $styles){
.address {
font-family: $font-family;
color: $primary-color;

//Loop through styles map
@each $property, $value in $styles {
#{$property}: $value;
}
}
}

And you'd call it like:

@include exampleTwo('Avenir', Red, (font-weight: 600, text-transform: uppercase));

Which would output:

.address {
font-family: "Avenir";
color: Red;
font-weight: 600;
text-transform: uppercase;
}

SassMeister Demo



Related Topics



Leave a reply



Submit