Skip to main content

Ditto: Making good use of Sass extends and placeholder selectors

Sass and Compass mixins and extends difference and placeholder selectors

When I first heard about the Sass @extend directive I think I got it all wrong, tried to use it, generally failed, and then swiftly forgot about them. The basic idea however, is rather simple:

Using the @extend directive, one selector can inherit the styles of another selector.

This serves to help reduce repetition of code. So let's see an example:

.foo {
  display: block;
  background: black;
  color: white;
}
.bar {
  @extend .foo;
  padding: 10px;
  margin-bottom: 20px;
}

So this means .bar is going to get all of those styles we gave .foo as well as the styles added specifically to .bar. Should spit out this CSS:

.foo, .bar {
  display: block;
  background: black;
  color: white;
}
.bar {
  padding: 10px;
  margin-bottom: 20px;
}

The problem with this is that it can quickly balloon out of control, I mean, what if something else extends .bar? what if something else extends that? Who is keeping track of this crap?

A better approach methinks, is to make use of a new Sass 3.2 feature: placeholder selectors.

Sass placeholder selectors

Placeholder selectors on their own will not be rendered to CSS but rather, lie in wait to be extended by other, real selectors. Using this approach you can set up these placeholders to house styles you will commonly use. Example:

%black-box {
  display: block;
  background: black;
  color: white;
}
.foo, .bar {
  @extend %black-box;
}
.bar {
  padding: 10px;
  margin-bottom: 20px;
}

Don't be discouraged that, in this example, there is actually more code to have to type because the advantage here is we have set up this %black-box style we can use all over the place. We also get to call it something memorable, rather than just the selector of the first thing that just-so-happens to use those styles.

Placeholder selectors are unambiguously there for reuse, and contain reusable snippets of code that we are going to be commonly using.

Using extends in conjunction with mixins

So if we get a bit clever about the way we use the @extend directive with mixins we can cut down a lot of repeated code. I'll use the internet's favourite mixin example - a custom button.

@mixin my-button($size: 15, $color: red) {
  @include inline-block;
  @include border-radius(5px);
  font-size: $size + px;
  background-color: $color;
}

Now I have made this mixin especially simple for the sake of example but you get the idea - using this mixin which takes arguments for $size and $color, we can create all the button styles for our site.

We can cut down a lot of repeated code by setting up placeholder selectors that we can @extend rather than running the mixin over and over, which needlessly repeats a lot of CSS.

%button {
  @include my-button;
}
%alt-button {
  @include my-button(15, green);
}
%big-button {
  @include my-button(25);
}

So now we can @extend these placeholder selectors instead of having to run the mixin every time we want these styles. Its gonna be a lot more efficient this way AND we have our placeholder selectors remembering the size and color of our various button styles so we don't have to!

Don't let the simplicity of the over-used button example throw you either, this approach can be used for loads stuff, even just reusing a complex box-shadow for example:

%shadow {
  @include box-shadow(1px 1px 1px 0px rgba(1, 1, 1, 0.15) inset);
}

Also, it's important to remember that extends will not work inside media queries.

Anyway, the world is basically your oyster now. Enjoy!