Search code examples
compass-sasssassmixinscss-animations

Including mixins using variables for name in Sass


I'm trying to call a mixin using a variable as the name, like so:

@include $mixin-name;

That doesn't seem so difficult...

I've seen a few people online mention wanting to do this. This ticket (http://dev.vaadin.com/ticket/9546) says "fixed", which I assumed meant that in Sass 3.2 it was possible, but this comment from the Sass group on Google Groups seems to suggest otherwise: http://goo.gl/HtdHu

I see what they're saying, it seems many people who are asking about it could quite easily solve their issues another way.

I can't think of another way for my issue though, so let me explain and maybe someone might have an idea or two?

CSS Animation

I've created a mixin for @keyframes so that I can call @include animation(); and get the full list of prefixed and official @keyframes

@include keyframes(reveal) {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

Gives me:

@-webkit-keyframes reveal {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

@-moz-keyframes reveal {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

@-ms-keyframes reveal {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

@-o-keyframes reveal {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

@keyframes reveal {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

Which is great! But if I use Compass transform in one of the keyframe states:

@include keyframes(round) {
    from {
        @include transform(rotateZ(-145deg));
        opacity: 1;
    }
    to {
        @include transform(rotateZ(-45deg));
        opacity: 0.5;
    }
}

then I end up getting something like:

@-webkit-keyframes round {
    from {
        -webkit-transform: rotateZ(-145deg);
        -moz-transform: rotateZ(-145deg);
        -ms-transform: rotateZ(-145deg);
        -o-transform: rotateZ(-145deg);
        transform: rotateZ(-145deg);
        opacity: 1;
    }
    to {
        -webkit-transform: rotateZ(-45deg);
        -moz-transform: rotateZ(-45deg);
        -ms-transform: rotateZ(-45deg);
        -o-transform: rotateZ(-45deg);
        transform: rotateZ(-45deg);
        opacity: 0.5;
    }
}

etc...

Prefixes inside prefixes

So, I know that this is pedantic, but it's really annoying me that I am declaring -webkit-animation, but then inside I have to declare all of the prefixes when what I want to do is only declare the same prefix as the keyframes and the official, eg:

@-webkit-keyframes round {
    from {
        -webkit-transform: rotateZ(-145deg);
        transform: rotateZ(-145deg);
        opacity: 1;
    }
    to {
        -webkit-transform: rotateZ(-45deg);
        transform: rotateZ(-45deg);
        opacity: 0.5;
    }
}

@-moz-keyframes round {
    from {
        -moz-transform: rotateZ(-145deg);
        transform: rotateZ(-145deg);
        opacity: 1;
    }
    to {
        -moz-transform: rotateZ(-45deg);
        transform: rotateZ(-45deg);
        opacity: 0.5;
    }
}

etc...

Mixin problems

So, I've created a mixin using @include experimental-value, but I can't automate it enough because

@function browser-is-prefix($browser, $prefix) {
    @if $browser == $prefix {
        @return true;
    } @else {
        @return false;
    }
}

@mixin transform-prefix($transform, $browser) {
    @include experimental-value(transform, $transform,
        browser-is-prefix($browser, -moz),
        browser-is-prefix($browser, -webkit),
        browser-is-prefix($browser, -o),
        browser-is-prefix($browser, -ms),
        false, true);
}

@mixin animation-name($browser) {
    from {
        @include transform-prefix(transform(translate(-25px,200px) scale(0.5)), $browser);
        opacity: 0;
    }
    to {
        @include transform-prefix(transform(translate(0,0) scale(0.5)), $browser);
        opacity: 1;
    }
}

Calling @include animation-name(-webkit) will work great and give us:

@-webkit-keyframes animation-name {
    from {
        -webkit-transform: translate(-25px,200px) scale(0.5);
        transform: translate(-25px,200px) scale(0.5);
        opacity: 0;
    }
    to {
        -webkit-transform: translate(0,0) scale(0.5);
        transform: translate(0,0) scale(0.5);
        opacity: 1;
    }
}

But I can't automate it!

To automate the process I wanted to be able to call a mixin, something like @include prekeyframes(animation-name); and have prekeyframes do exactly what the keyframes mixin does above, except instead of blindly including the @content of the, it will include the mixin and only use the prefixes it should based on what prefix the @keyframes has:

@mixin prekeyframes($name) {
    $prefixes : -webkit-, -moz-, -o-, -ms-, '';
    @each $prefix in $prefixes {
        @include $name($prefix);
    }
}

This, of course, throws an error. If I interpolate $name (@include #{$name}($prefix)), it's still an issue.

So, I could just live with the extra transform values, or I could do it all manually, but I've always been really impressed with the flexibility of Sass and this seems really limiting to me...

To summarise:

  • I want to define the animation once, in one place.
  • I want to be able to @include transform within that animation.
  • I want the correctly prefixed transform inside the correctly prefixed @keyframes

I've spent a few hours on this now, and I'll continue to fiddle... But if anyone has similar experiences or problems, I'd love to know...

Thanks!


Solution

  • Check out Bourbon (relevant code and documentation), it's fantastic.