Sass: Mixin or Placeholder?

Tweet

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.

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

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://lunelson.net/ Lu Nelson

    I find that using @includes rather than @extends is easier on my mental model of what I’m doing, esp. for keeping track of where things are in the source order. I would say another option is to use temporary mixins for repetitive code, strictly during development, and for production to abstract them out to classes in the html. This can actually be even lighter overall than usings @extends.

  • Maxime Fabre

    You should mention that you can’t use placeholders in media queries too.

    • Luca Rosaldi

      In a past article I read that there was a trick to use @extend in media queries, but I can’t just remember where.

    • Justin Silva

      Using placeholders in media queries works for me (with Sass Gem v3.2.12), but it’s deprecated and will throw an error in v3.3.

  • Roman Blöth

    Thanks for your refresher! You might update your article – while the title says it’s about the differences between mixins and placeholders, you point out that placeholders alone are not compiled into CSS and quite useless if not referenced elsewhere. But the exact same thing is true for mixins! They are not compiled into CSS and useless unless referenced elsewhere. To novices this might be more confusing than of any help, as they might believe that mixins behave different in these respects.

    • LouisLazaris

      Hi @romanblth:disqus, thanks for pointing that out. You might be right, I’ll see if Hugo thinks we can improve the wording of that.

      In my opinion, when he discusses the definition of placeholders, I don’t think it was his intention to say that the fact that placeholders don’t compile is different from mixins. I think he was just explaining what they are from a different angle. Also, I think most people are much more familiar with mixins, so he probably didn’t feel the need to mention that the mixin isn’t compiled without a reference. So it was more about explaining each one, not necessarily saying that these features make them opposites or anything like that.

      Nonetheless, I think you are correct that it might be a little confusing. Thanks for spotting that!

    • http://hugogiraudel.com/ Hugo Giraudel

      A mixin is very similar to a function in a lot of various ways. I think it’s common knowledge that functions do not do anything until they get called. Thus, same goes for mixins. I’m not sure it might be worth adding it, but I guess it couldn’t arm.

      Meanwhile, a placeholder looks *very* similar to a regular CSS selector so I think it’s important to point out the main difference between a class and a Sass placeholder: one got compiled, the other not.

      • LouisLazaris

        Meanwhile, a placeholder looks *very* similar to a regular CSS selector so I think it’s important to point out the main difference between a class and a Sass placeholder

        Yes, that’s exactly what I was thinking too. But I’ll just add a small correction to the wording, to avoid the confusion Roman is pointing out.

  • LouisLazaris

    Lydia Quintanilla There are no browsers that support these. This is using a language called “Sass”, which (to simplify the explanation) uses back-end programming to create valid CSS for you. Basically, all the code above needs to be run through a Sass compiler first, before it will work. If you do some reading on Sass, you’ll see how you can do that.

    If you want to test these in a browser to see them compile to regular CSS, you can use a tool like SassMeister, which does it right on the page for you. But it won’t show what the browser sees, it will just show you the valid output CSS. Hope that makes sense. :)

  • sebthemonster

    Excellent article and very helpful. Thanks a lot!

  • http://emergingtruths.com/ larry

    Thanks for explaining this distinction. As someone who is just getting started with sass, and is concerned with size of the generated CSS, this article has taught me something I will use right away.

  • http://www.be.net/brunodsgn Bruno Rodrigues

    Great explanation! =D

  • John Cobb

    Really nice overview Hugo!

    I’ve been working on a large scale SASS project and encountered a ‘gotcha’ which should be kept in mind when using placeholders. If you’re using placeholders for common abstractions (such as your %center example) and adding additional rules to that selector, you end up with two references to those selectors in your stylesheet. This can potentially become an issue in IE9 and below as those browsers have a limitation of 4095 selectors in a single stylesheet – all rules after that will be ignored. Sounds like a high number, but on a large project it can definitely be hit :)

    • http://hugogiraudel.com/ Hugo Giraudel

      I have the problem as well at work. I ended splitting the stylesheet in 2 parts to avoid the issue… That kinda sucks.

  • http://cabgfx.com/ Casper Klenz-Kitenge

    Great explanation!

  • thefairystamp

    I have actually troubles understanding the difference between using @extend on regular classes, and using it on %placeholders – well, besides, that if you dont use the placeholder, it is not compiled. But besides that, does it anything else?

    I am defining classes in my _font.scss partial for different headline and paragraph styles. When I style these, I can insert the class in the markup, to see if it looks good, and then remove it, to @extend it via scss.

    • http://hugogiraudel.com/ Hugo Giraudel

      > I have actually troubles understanding the difference between using @extend on regular classes, and using it on %placeholders – well, besides, that if you dont use the placeholder, it is not compiled. But besides that, does it anything else?

      No, it’s exactly the same. :)

  • http://aaron-jensen.com Aaron Jensen

    There are a few other things to keep in mind when deciding whether or not to use placeholders/extend.

    1) extends are slower to compile. Last I looked at the source, the time extends took was O(# of extends * # of selectors). In my simple benchmark w/ 200 extends/mixins and 2400 selectors, extends took 20% longer to compile. This probably isn’t an issue w/ sassc or on smaller projects, but it may make a difference to some w/ larger projects.

    2) extends make debugging styles much more tricky. When you look at something in the browser you see a massive list of selectors and the rule associated w/ that placeholder. When you look at the debug lines or sourcemaps, they point back to the placeholder line and you’re left to reverse engineer where that placeholder was extended in your particular case. This can make tracking stuff down tricky in larger/complicated projects.

    3) Someone mentioned the selector limit already, that’s a real thing. 4096 selectors isn’t that many on a big site, especially when using extends for lots of things.

    4) Once the css is gzipped, the space savings between extends and mixins is probably nominal. It depends on your use, but I wouldn’t let space be your reason for doing extends now a days.

    • http://aaron-jensen.com Aaron Jensen

      I guess I should say something about when I think it’s a good idea to use extends… I’d say probably when you’re actually using it for a purpose that its name indicates. When you want to extend something. The table/table-zebra example was a good one. Then again, I typically would apply two classes in that case rather than relying on extend……

  • http://emergingtruths.com/ larry

    just a note of appreciation of your excellent posts. I’ve read quite a few of them now, and every one is excellent, both in presentation as well as efficacy.