Sass and Bootstrap - Mixins VS. @Extend

SASS and Bootstrap - mixins vs. @extend

The big difference between @extend and a mixin is the way the css is compiled. It doesn't look like much in simple examples, but the differences and implications are significant and can be a real headache in the wild if used carelessly. @extend is a little bit like fools gold, looks great at first, but ...

Let's look at a simple example:

@extend

.row {
width: 50px;
}
.new-row {
@extend .row;
}
.another-row {
@extend .row;
}

compiles into:

.row,
.new-row,
.another-row {
width: 50px;
}

mixin

@mixin row() {
width: 50px;
}
.new-row {
@include row();
}
.another-row {
@include row();
}

compiles into:

.new-row {
width: 50px;
}
.another-row {
width: 50px;
}

A mixin includes the properties everywhere it is hit - copying them each time - whereas an @extend groups the selectors and defines the properties once. This isn't immediately obvious, because the difference is in the compiled css but it has some important implications:

Load order

With @extend the selectors will be grouped at the first point in the sass where they are encountered which can lead to some weird over-riding. If you define a selector and use @extend to bring in a property to and try to override a property defined earlier in your sass, but after the point at which the extended properties are grouped in the css then the override will not work. This can be quite perplexing.

Consider this logically ordered set of css definitions and the likely HTML: <div class='row highlight-row'></div>:

.red-text {
color: red;
}
.row {
color: green;
}
.highlight-row {
@extend .red-text;
}

compiles into:

.red-text,
.highlight-row {
color: red;
}
.row {
color: green;
}

So even though the sass ordering makes it look like the row colour would be red, the compiled css will make it green

Poor groupings

@extend can result in poorly grouped selectors in the resulting css. You can end up with thirty or forty unrelated things all sharing the same property for example. Using @extend for fonts is a good example of this.

Nesting

If you are using deeply nested sass (which is not good, btw) and you use @extend you will duplicate the fully nested selector for every @extend you use, resulting in bloated css. I've seen this a lot:

.selector-1 .selector-2 .selector-3 .selector-4,
.selector-1 .selector-2 .selector-3 .selector-4 a,
.selector-1 .selector-2 .selector-3 .selector-4 li,
.selector-1 .selector-2 .selector-3 .selector-4 td {
font-family: arial;
}

If you're new to SASS it pays to look at the compiled css.

Media queries

@extend do not work inside media queries, because media queries are not selectors.

Conclusion

My rule of thumb is to use an @extend over a mixin if you have no parameters and if you can reasonably define the @extend and share it amongst a few tightly related selectors that exist nearby in the sass, for example, in the same file that defines a sass module. Buttons are a good example of well used @extend:

%button {
padding: 10px;
}
.call-to-action {
@extend %button;
background-color: $green;
}
.submit {
@extend %button;
background-color: $grey;
}

The best article to help make the choice is here

PS, the % sign is a use of placeholder extends

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 generates two separate rules for same class using extend and mixins

This is the actual behaviour and a use-case of Sass @extend.

Explanation

To make it clear, update your code as below

%red-color{
color: red
}

@mixin btn-structure ($text-case: null, $text-shadow: null, $decoration: none ){
display: inline-block;
text: {
decoration: $decoration;
transform: $text-case;
shadow: $text-shadow
}
}

.link-btn{
@extend %red-color;
@include btn-structure($text-case: 'uppercase', $decoration: underline);
}

.test-class{
@extend %red-color;
@include btn-structure($text-case: 'uppercase', $decoration: underline);
}

Which would compile as,

.link-btn, .test-class {
color: red;
}

.link-btn {
display: inline-block;
text-decoration: underline;
text-transform: "uppercase";
}

.test-class {
display: inline-block;
text-decoration: underline;
text-transform: "uppercase";
}

As you could see, @extend is used to "share a set of CSS properties from one selector to another", which can be clubbed together (.link-btn, .test-class). Whereas, @include is used to insert the styles where ever required, which is not clubbed.

Solution

For your requirement, you can resort to @include and declare a mixin @mixin red-color as below,

%red-color{
color: red
}

@mixin red-color{
color: red
}

@mixin btn-structure ($text-case: null, $text-shadow: null, $decoration: none ){
display: inline-block;
text: {
decoration: $decoration;
transform: $text-case;
shadow: $text-shadow
}
}

.link-btn{
@include red-color;
@include btn-structure($text-case: 'uppercase', $decoration: underline);
}

Output

And the compiled css will be,

.link-btn {
color: red;
display: inline-block;
text-decoration: underline;
text-transform: "uppercase";
}

Hope this helps.

How to reuse (extend) bootstrap css classes in rails/sass

Problem was because I also imported files via sass require/require_tree command.

 *= require_tree ./autoinclude

If all files are imported via @import function, everything works!

@import "autoinclude/*";


Related Topics



Leave a reply



Submit