Lesson Weekend

In Intro we learned that when the same type of object and functionality is needed over and over again, we can create constructors and prototypes to DRY up our code, and ensure these objects are consistent. Like this code we wrote back in Intro:

function Contact(first, last) {
  this.firstName = first;
  this.lastName = last;
}

Contact.prototype.fullName = function() {
  return this.firstName + " " + this.lastName;
}

The constructor here can create many consistent Contact objects, all able to have the fullName() method called on them. Classes in programming languages, if you've encountered them, work similarly.

Sass can extend similar programmatic functionality into our styles with something called mixins. Mixins are blocks of Sass that can be defined once, then called and reused in multiple spots to keep styles DRY, similar to the JavaScript code above.

Mixins

The Sass Documentation entry on Mixins defines mixins as follows:

Mixins allow you to define styles that can be re-used throughout the stylesheet without needing to resort to non-semantic classes like .float-left. Mixins can also contain full CSS rules, and anything else allowed elsewhere in a Sass document. They can even take arguments which allows you to produce a wide variety of styles with very few mixins.

Pretty cool, right? Let's take a peek at what they look like.

Writing Mixins

Mixins are defined using a @mixin directive. In programming, a directive is a special keyword that tells a compiler (like the Sass compiler that turns our Sass input into CSS output) that the code following it is special, and needs to be treated differently.

In this case, the @mixin directive tells the compiler that code that follows will be reused throughout the stylesheet, and to insert this code wherever the mixin's name is called.

The @mixin directive is followed by a name and a set of curly braces, as seen in this example from Sass documentation:

@mixin large-text {

}

Sass code we want to be reusable as part of the mixin is defined in the brackets:

@mixin large-text {
  font: {
    family: Arial;
    size: 20px;
    weight: bold;
  }
  color: #ff0000;
}

Calling Mixins

Once we define a mixin, we can call it anywhere in our Sass with the @include directive.

For instance, if wanted styles in our large-text mixin to apply to a title with the class .page-title, we could define a rule selecting .page-title, then @include the mixin in the rule:

.page-title {
  @include large-text;
  padding: 4px;
  margin-top: 10px;
}

Because the large-text mixin was included, this Sass will be compiled and output as the following:

.page-title {
  font-family: Arial;
  font-size: 20px;
  font-weight: bold;
  color: #ff0000;
  padding: 4px;
  margin-top: 10px;
}

The rule now contains both all .page-title style declarations, and all style declarations from the large-text mixins.

Mixin Arguments

Just like JavaScript functions, mixins can be defined to accept arguments, too! Arguments are available throughout the mixin's styles by referring to their variable names. For instance, pretend the text we styled with the large-text mixin from above needs to be different colors in different places. We could add a color argument:

@mixin large-text($color) {
  ...
}

Notice the argument must be preceded by a $, just like plain Sass variables.

Once we declare an argument, we can call it within the mixin's block of styles:

@mixin large-text($font-color) {
  font: {
    family: Arial;
    size: 20px;
    weight: bold;
  }
  color: $font-color;
}

Then whenever we @include the mixin we must pass in the relevant argument, just like calling a JavaScript function that takes an argument:

.page-title {
  @include large-text(red);
  padding: 4px;
  margin-top: 10px;
}

This Sass would then compile into the following CSS:

.page-title {
  font-family: Arial;
  font-size: 20px;
  font-weight: bold;
  color: red;
  padding: 4px;
  margin-top: 10px;
}

Like JavaScript, Sass mixins can also accept any number of arguments, as long as they're separated by a comma.

Default Arguments

We can also specify default arguments. A default argument is a value that will be used as an argument if no other value is provided when the mixin is called.

For instance, let's assume we decided text the large-text mixin formats should actually be #ff0000-colored in almost every case, except for one or two exceptions. We could make #ff0000 the default $font-color argument like this:

@mixin large-text($font-color: #ff0000) {
  font: {
    family: Arial;
    size: 20px;
    weight: bold;
  }
  color: $font-color;
}

Then we could @include this mixin in other Sass rules:

.page-title {
  @include large-text;
  padding: 4px;
  margin-top: 10px;
}

Because the rule above did not provide an argument, it will be compiled using the default color value:

.page-title {
  font-family: Arial;
  font-size: 20px;
  font-weight: bold;
  color: #ff0000;
  padding: 4px;
  margin-top: 10px;
}

But if we did provide this optional argument in another rule...

.content-heading {
  @include large-text(red);
  padding: 14px;
}

...it will use the color provided as an argument instead of the default:

.content-heading {
  font-family: Arial;
  font-size: 20px;
  font-weight: bold;
  color: red;
  padding: 14px;
}

We recommend checking out out the following 8 minute video depicting mixins in a full-fledged codebase. It's not required, and doesn't cover anything we haven't discussed above, but will allow us to observe how mixins are used in a full-fledged codebase:

Keeping Vendor Prefixes DRY

Mixins have a lot of power to keep styles DRY. So anytime you find yourself reusing similar Sass, try defining a mixin! For instance, here's a clever mixin usage to keep in mind:

Remember when we discussed vendor prefixes in the pre-work? In that lesson we learned that when we use a prefixed property in a CSS rule, it's best practice to list all prefixed declarations for all browsers our application must support, before the un-prefixed version.

The Browser Compatibility and Vendor Prefixes used the following example:

css.sample-parent-container {
  -moz-border-radius: 10px;
  -webkit-border-radius: 10px;
  -o-border-radius: 10px;
  border-radius: 10px;
}

If we needed border-radius styles in multiple rules we could package all these prefixes into a single mixin:

@mixin border-radius($radius) {
  -webkit-border-radius: $radius;
     -moz-border-radius: $radius;
      -ms-border-radius: $radius;
          border-radius: $radius;
}

Then @include it with the relevant argument wherever we need a border radius:

.box {
  @include border-radius(10px);
}

This would compile into the following CSS in our output file:

.box {
  -webkit-border-radius: 10px;
  -moz-border-radius: 10px;
  -ms-border-radius: 10px;
  border-radius: 10px;
}

Can you think of other clever use cases for Sass mixins? We'll explore more throughout the week as we uncover other powerful Sass functionality.