HTML & CSS - - By Hugo Giraudel

Sass: Mixin or Placeholder?

When I started playing with Sass about a year and a half ago, one thing that took me some time to understand was the difference between including a mixin and extending a placeholder. Actually even the notion of placeholder was kind of black magic voodoo for me back then.

If you are in a similar situation, don’t worry, because I will try to enlighten the path. Today we’ll learn what exactly a mixin is for, and when to use a Sass placeholder. You’ll understand they both serve different purposes and shouldn’t be confused.

Note: While I am going to talk about Sass, this article could well apply to any other CSS preprocessor, whether it be Stylus, LESS, or whatever you happen to use. The technologies are generally doing the same thing, so feel free to adapt the content of this article to the tool of your choice.

First we should probably do some catch up on what we are talking about when we refer to Sass placeholders and mixins, so let’s do that now.

Mixin it Up

A mixin is a directive that allows you to define multiple rules depending on several arguments. Think of it as a function that would dump CSS content instead of returning a value. Here is the definition from the Sass Reference:

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.

Now that we’ve covered terminology, let’s say you spot a couple of declarations that are repeated multiple times across your stylesheet. You know that code repetition is bad and you are familiar with the DRY (Don’t Repeat Yourself) concept. To correct this, you can write a mixin for those repeated declarations:

@mixin center() {
  display: block;
  margin-left: auto;
  margin-right: auto;
}

.container {
  @include center();
  /* Other styles here... */
}

/* Other styles... */

.image-cover {
  @include center;
}

Note: If you don’t pass an argument to a mixin, you can omit the parentheses. In fact, you can even omit them in the @mixin definition.

With this newly created mixin way you don’t have to repeat those three lines every time you need to center an element; you simply include the mixin. Pretty convenient, isn’t it!

Sometimes you want a mixin to build what you would call a “shorthand” for a couple of properties. For example, width and height. Aren’t you tired of writing both lines over and over again? Especially when both have the same value? Well let’s deal with that using a mixin!

@mixin size($width, $height: $width) {
  width: $width;
  height: $height;
}

Quite simple, isn’t it? Note how we made the $height parameter optional by defaulting it to have the same value as the $width directly in the mixin signature. Now whenever you need to define dimensions for an element, you can just do this:

.icon {
  @include size(32px);
}

.cover {
  @include size(100%, 10em);
}

Note: another good example of a syntactic-sugar mixin would be this one I made to avoid writing top, right, bottom, left and position every time I want to use a different position system than static.

Know Your Placeholder

Placeholders are kind of a weird thing. They are classes that aren’t output when your SCSS is compiled. So then you might think “What’s the point?”. Actually the point would be minimal if it wasn’t for the @extend directive. But first things first. This is how you write a placeholder:

%center {
  display: block;
  margin-left: auto;
  margin-right: auto;
}

Editors Note: Like a placholder, a mixin is likewise useless unless its referenced, so this section is not necessarily saying they are different in that respect, but this is only clarifying that even though it looks similar to a CSS declaration block, it won’t get compiled on its own.

Basically you write it exactly like a class except you prefix it with the % symbol instead of a dot. Also, it follows the same naming rules as classes.

Now if you try to compile your SCSS, you won’t see this sample of code in the generated file. As I’ve said: placeholders are not compiled to the source.

So for now, this placeholder is absolutely useless. You can’t make any use of it unless you look at the @extend side. The @extend directive aims to inherit properties from a CSS selector / SCSS placeholder. Here is how you use it:

.container {
  @extend %center;
}

By doing this, Sass will get the content of the %center placeholder and apply it to .container (even if that’s actually not quite how it’s done — but that doesn’t matter right now). As I said, you can also extend existing CSS selectors (in addition to SCSS placeholders) like this:

.table-zebra {
  @extend .table;

  tr:nth-of-type(even) {
    background: rgba(0,0,0,.5);
  }
}

This is a very common use case for the @extend directive. In this case, we ask for the .table-zebra class to behave exactly like the .table class and then we add the zebra-specific rules. Extending selectors is very convenient when you develop your site/application in modular components.

Watch AtoZ: Sass

Learn Sass letter by letter

Which One to Use?

So the question remains: which one should you use? Well, as for everything in our field: it depends. It depends on the context and what you are ultimately trying to do.

The best advice would be: if you need variables, use a mixin. Otherwise, extend a placeholder. There are two reasons for this:

  • First, you can’t use variables in a placeholder. Actually you can but you cannot pass variables to your placeholders so you can’t generate context-specific CSS like you would do with a mixin.
  • Second, the way Sass handles mixins makes them very inconvenient when you don’t use them with contextual variables. To put it simply: Sass will duplicate the output of the mixin every time you use it, resulting not only in duplicated CSS but also a bigger stylesheet.

Consider the very first example in this article:

@mixin center {
  display: block;
  margin-left: auto;
  margin-right: auto;
}

.container {
  @include center;
}

.image-cover {
  @include center;
}

This will result in the following CSS output:

.container {
  display: block;
  margin-left: auto;
  margin-right: auto;
}

.image-cover {
  display: block;
  margin-left: auto;
  margin-right: auto;
}

Notice the duplicate CSS? It won’t do much harm if it’s only three lines duplicated, but if you have mixins dumping dozens of CSS lines and used multiple times across a project, those three lines could easily become 300. What if we revamp our example to instead use a placeholder?

%center {
  display: block;
  margin-left: auto;
  margin-right: auto;
}

.container {
  @extend %center;
}

.image-cover {
  @extend %center;
}

Now this is the generated CSS:

.container, .image-cover {
  display: block;
  margin-left: auto;
  margin-right: auto;
}

Much better! The compilation takes advantage of selector grouping without any repeated styles. So whenever you want to avoid writing the same properties over and over, knowing that they won’t ever change, it is a good idea to extend a placeholder. This will result in a much leaner compiled stylesheet.

On the other hand if you are willing to write the same properties at various places but with different values (sizes, colors, etc.) a mixin is the best way to go. If you have a group of both fixed values and variables values, you should try a combination of both.

%center {
  margin-left: auto;
  margin-right: auto;
  display: block;
}

@mixin skin($color, $size) {
  @extend %center;
  background: $color;
  height: $size;
}

a { @include skin(pink, 10em) }
b { @include skin(blue, 90px) }

In this case, the mixin is extending the placeholder for fixed values instead of dumping them directly in its body. This generates clean CSS:

a, b {
  margin-left: auto;
  margin-right: auto;
  display: block;
}

a {
  background: pink;
  height: 10em;
}

b {
  background: blue;
  height: 90px;
}

Conclusion

So there you have it. I hope I’ve made it clear not only what mixins and placeholders are for, but also when you should use them and what effect they will have on the compiled CSS.

If you have anything to add regarding your experiences with these features in CSS preprocessors, feel free to share them in the comments.

This article was translated into French by Pierre Choffé on La Cascade