In Sass, What's The Difference Between The @Mixin and @Extend Directives

In Sass, what's the difference between the @mixin and @extend directives?

From http://blog.nakulrajput.com/mixins-extends-and-placeholders/:

@mixin

Here is how mixins work. Definition and usage:

@mixin awesome {
width: 100%;
height: 100%;
}

body {
@include awesome;
}

p {
@include awesome;
}

The snippets above produce the following CSS:

body {
width: 100%;
height: 100%;
}

p {
width: 100%;
height: 100%;
}

To make things a little bit more interesting, we could make our mixin accept parameters. Even better, we are able to define default values if the mixin is called without arguments.

@mixin awesome($w: 100%, $h: 100%) {
width: $w;
height: $h;
}

body {
@include awesome(960px);
}

p {
@include awesome;
}

The result will be similar, but the width of the body is different.

body {
width: 960px;
height: 100%;
}

p {
width: 100%;
height: 100%;
}

If you use mixins, the styles in them are duplicated for each selector.

Mixins are very helpful if you need to change or calculate something in the final output, for example if you need to apply border-radius to several elements.

However, in some other cases there is a lot of duplicative code, which could be avoided if you use @extend.

**@extend**

.awesome {
width: 100%;
height: 100%;
}

body {
@extend .awesome;
}

p {
@extend .awesome;
}

It's similar, isn't it. In Sass it looks almost identical, but the CSS the result is:

.awesome, body, p {
width: 100%;
height: 100%;
}

Shorter than the version using a mixin. You can't pass parameters during the extending, but that's not the idea actually.

@extend should be used in those places where you want to share properties between the elements.

When to use @extend and @mixin in Sass

You are on the right way.

You should use a mixin when you want the output to change depending on how you call it.

So, in some cases using a %placeholder and @extend would have produced less CSS.
It is important to realise that @extend creates relationships. Whenever you use @extend, you are transplanting a selector elsewhere in your stylesheet in order for it to share traits with other selectors that are also being transplanted.

EXTEND

e.g.

.awesome {
width: 100%;
height: 100%;
}

body {
@extend .awesome;
}
p {
@extend .awesome;
}

Which returns:

.awesome, body, p {
width: 100%;
height: 100%;
}

MIXIN

@mixin awesome {
width: 100%;
height: 100%;
}

body {
@include awesome;
}
p {
@include awesome;
}

Will return:

body {
width: 100%;
height: 100%;
}
p {
width: 100%;
height: 100%;
}

But with mixins you can have parameters like:

@mixin awesome($w: 100%, $h: 100%) {
width: $w;
height: $h;
}

body {
@include awesome(960px);
}
p {
@include awesome;
}

Which returns:

body {
width: 960px;
height: 100%;
}
p {
width: 100%;
height: 100%;
}

Hope this was helpful!

Additional:

The @extend directive isn’t flexible. You can’t pass arguments to it so what’s there is there. There are also limitations on using @extend inside @media directives and you can’t extend a selector across different media directives using @extend.

The main advantage to using mixins is the power and flexibility they have because they can accept arguments. Naturally, if you want to pass arguments, you’ll have to choose @mixin over @extend.

They are kind of interchangeable, it depends also if you want/have to stick to DRY CSS or not.

EXTEND

%hide-text {
text-indent: -9999px;
overflow: hidden;
}

.foo {
@extend %hide-text;
}

.bar {
@extend %hide-text;
}

.baz {
@extend %hide-text;
}

Nice and clean result:

.foo, .bar, .baz {
text-indent: -9999px;
overflow: hidden;
}

MIXIN

@mixin hide-text {
text-indent: -9999px;
overflow: hidden;
}

.foo {
@include hide-text;
}

.bar {
@include hide-text;
}

.baz {
@include hide-text;
}

Result:

.foo {
text-indent: -9999px;
overflow: hidden;
}

.bar {
text-indent: -9999px;
overflow: hidden;
}

.baz {
text-indent: -9999px;
overflow: hidden;
}

You can see there's no relation between the CSS selectors.

Using @include vs @extend in Sass?

Extends do not allow customization, but they produce very efficient CSS.

%button
background-color: lightgrey
&:hover, &:active
background-color: white

a
@extend %button

button
@extend %button

Result:

a, button {
background-color: lightgrey;
}
a:hover, button:hover, a:active, button:active {
background-color: white;
}

With mixins, you get duplicated CSS, but you can use arguments to modify the result for each usage.

=button($main-color: lightgrey, $active-color: white)
background-color: $main-color
border: 1px solid black
border-radius: 0.2em

&:hover, &:active
background-color: $active-color

a
+button

button
+button(pink, red)

Results in:

a {
background-color: lightgrey;
border: 1px solid black;
border-radius: 0.2em;
}
a:hover, a:active {
background-color: white;
}

button {
background-color: pink;
border: 1px solid black;
border-radius: 0.2em;
}
button:hover, button:active {
background-color: red;
}

Please follow this consecutive set of code examples to see how you can make your code cleaner and more maintainable by using extends and mixins effectively: http://thecodingdesigner.com/posts/balancing

Note that SASS unfortunately does not allow using extends inside media queries (and corresponding example from the above link is wrong). In the situation where you need to extend based on media queries, use a mixin:

=active
display: block
background-color: pink

%active
+active

#main-menu
@extend %active // Active by default

#secondary-menu
@media (min-width: 20em)
+active // Active only on wide screens

Result:

#main-menu {
display: block;
background-color: pink;
}

@media (min-width: 20em) {
#secondary-menu {
display: block;
background-color: pink;
}
}

Duplication is inevitable in this case, but you shouldn't care too much about it because web server's gzip compression will take care of it.

PS Note that you can declare placeholder classes within media queries.

Update 2014-12-28: Extends produce more compact CSS than mixins do, but this benefit is diminished when CSS is gzipped. If your server serves gzipped CSS (it really should!), then extends give you almost no benefit. So you can always use mixins! More on this here: http://www.sitepoint.com/sass-extend-nobody-told-you/

Sass directives mixin, include, =, and +

This is old syntax for the .sass files (Pre SASS3)

see here under "Mixins": http://sass-lang.com/docs/yardoc/file.SCSS_FOR_SASS_USERS.html

Moving forward, you should use @mixin and @include, unless you have a reason for continuing to use the legacy format.

SASS: adding extend in mixin

The problem is that you forgot the # when interpolating within the mixin

The lines of code should look like this

@mixin svg-sprite($sprite) {
@extend #{$sprite};
@extend #{$sprite}-res;
}

SASS: Returning set of different CSS properties from a mixin or function

I found a logic for this problem using mixins.

I have created a Mixin that accepts all the params passed as a list, hence declaration ends in ...
This solution also covers optional CSS properties which may not be included in all the classes but majority of them.

styles.scss

.blog-content {
// for .destination
@include text-properties("destination", "EpicRide", normal, 65px, $standard-black, none, 2.8px, '', '');
// for .intro-title
@include text-properties("intro-title", "Montserrat", 500, 14px, $standard-black, uppercase, 2.8px, 5px 5% 0, '');
// for .place
@include text-properties("place", "Montserrat", 500, 14px, $standard-black, uppercase, 2.8px, inherit, '');
// for .description
@include text-properties("description", "Lora", 400, 16px, $standard-light, none, 0, 0 5%, 20px);
}

_variables.scss

@mixin text-properties($args... ) {  // args has a list of arguments
$class: nth($args, 1); // nth is used to extract 'nth' argument's value
$font-family: nth($args, 2);
$font-weight: nth($args, 3);
$font-size: nth($args, 4);
$color: nth($args, 5);
$text-transform: nth($args, 6);
$letter-spacing: nth($args, 7);
$padding: nth($args, 8);
$margin-top: nth($args, 9);

.#{$class} { // create class dynamically. Mention the variable name inside #{} to fetch it's value
font-family: $font-family;
font-weight: $font-weight;
font-size: $font-size;
color: $color;
text-transform: $text-transform;
letter-spacing: $letter-spacing;
@if $padding != '' { // condition to ensure property is added only if valid param is passed.
padding: $padding;
}
@if $margin-top != '' {
margin-top: $margin-top;
}
}
}

Resultant CSS

.blog-content .destination {
font-family: "EpicRide";
font-weight: normal;
font-size: 65px;
color: rgba(0, 0, 0, 0.9);
text-transform: none;
letter-spacing: 2.8px;
}
.blog-content .intro-title {
font-family: "Montserrat";
font-weight: 500;
font-size: 14px;
color: rgba(0, 0, 0, 0.9);
text-transform: uppercase;
letter-spacing: 2.8px;
padding: 5px 5% 0;
}
.blog-content .place {
font-family: "Montserrat";
font-weight: 500;
font-size: 14px;
color: rgba(0, 0, 0, 0.9);
text-transform: uppercase;
letter-spacing: 2.8px;
padding: inherit;
}
.blog-content .description {
font-family: "Lora";
font-weight: 400;
font-size: 16px;
color: rgb(var(--LightColorRGB));
text-transform: none;
letter-spacing: 0;
padding: 0 5%;
margin-top: 20px;
}

This solution has one limitation though, an increase in the number of CSS properties would increase the mixin code.

I hope this will solve your issue. Suggestions/Comments are welcomed to improve this logic.

Is using mixin with no parameter is more efficient than using extend

Extending a class will add the styles just once, and add all the selectors that the style needs to be applied to alongside where it's defined. A mixin on the other hand, will copy the styles individually to each selector where you've included it.

Here's what I mean.

Mixin

@mixin mixin(){
color: blue;
}

.selector-1{
@include mixin();
}

.selector-2{
@include mixin();
}

Compiles to

.selector-1 {
color: blue;
}
.selector-2 {
color: blue;
}

Extending a class

.extend{
color: blue;
}

.selector-1{
@extend .extend;
}

.selector-2{
@extend .extend;
}

Compiles to

.extend, .selector-1, .selector-2 {
color: blue;
}

So in terms of efficiency, extending a class will result in a smaller compiled file size, which is why Sass offers the extend feature in the first place.

What is the point of sass @extend?

In most cases @extend makes the css output smaller than if you would use a mixin. But the most efficient way is to put that @extend class in the html and not extend it to an element which does not have that class. Eventually the DOM might get messy, but it's faster.



Related Topics



Leave a reply



Submit