Mastering Sass extends and placeholders

13 Nov 2012 by Chris
9 Comments
Sass extends and placeholders

It all begins with the @extend...

It’s probably a safe bet you have heard of Sass, and for good reason too as using it really gives you an opportunity to organise and improve how you develop CSS. Before working at Primate I hadn’t had the chance to use Sass as part of my day job but I had experimented a little outside work. Chances are, quite a few developers are in the same boat right now. It’s also really worth mentioning that you don’t need to be a Ruby/Rails developer to use Sass! There are some great tools out there which are incredibly easy to use and let you use Sass without running Rails. So if you’re a PHP developer, or you build static sites, theres no reason you can’t get involved.

Now after telling you how awesome Sass is, I’m also going to tell you how you can use it to produce some really ugly and bloated CSS, ha! Yes it’s true you can use Sass to produce some nasty CSS… but only if you let it.

One of the cool and useful features of Sass is the @extend keyword. It gives you the opportunity to keep your CSS DRY and avoid the bloat you may experience using mixins. @extends let you define a block of style declarations and then let you apply these styles to other selectors throughout your Sass files. There are two ways this can be achieved in plain CSS – you either copy all the style declarations into multiple style blocks or you add multiple selectors to a generic style block. @extends do the latter. Anytime you use the @extend keyword, the containing selector is added to a style block, along with any other selectors that extend the same thing.

@extends are really handy and they let you introduce code to re-use in your stylesheets in the same way you would do if you were coding straight CSS. If you’re not strict on how you use them, then you’re probably going to introduce a lot of unwanted selectors into your CSS.

Should you @extend a class selector?

When you read the @extend documentation, the majority of the samples give an example of extending a class selector in the following way: @extend .error;. In the long run using a class selector as your style block identifier is not the best idea, especially if it’s quite a generic and regularly used class. A problem of using a standard class selector as your @extend identifier is that you extend any styles that feature that selector anywhere in your Sass.

As an example, let’s imagine you define a base block of styles with the class .standard_button. It’s a generic button used throughout the site which is styled with gradients, rounded corners, shadows, and is generally just pretty awesome. Initially you assign styles by giving elements in your HTML this class and often throughout the build you have to tweak the position of a button so you do this in CSS by using the .standard_button identifier. This is just plain old CSS so far. As a way of making your markup cleaner, you sometimes also use @extend .standard_button to reduce class declarations within your HTML. Perhaps it seems strange to use both @extends and class declarations within your HTML but I believe it’s something quite a few developers might find themselves doing. Unfortunately using both approaches will lead to generated CSS you don’t want.

As an example look at the code sample below which shows both the use of @extend and multiple selectors referencing the base class selector:


.standard_button {
  display: block; padding: 10px; background: green; // Other styles...
}

#sidebar .signup .standard_button { margin-top: 22px; }

#registration .standard_button { margin-bottom: 33px; }

.article a { @extend .standard_button; }

When compiled the generated CSS will be like the following (note the selectors for the final two styles):


.standard_button, .article a {
  display: block;
  padding: 10px;
  background: green;
}

#sidebar .signup .standard_button, #sidebar .signup .article a, .article #sidebar .signup a {
  margin-top: 22px;
}

#registration .standard_button, #registration .article a, .article #registration a {
  margin-bottom: 33px;
}

The selectors applied to the final two blocks of styles are just wrong and the chances are pretty high that you don’t want it in your CSS. I bet when you added .article a { @extend .standard_button; } you only wanted to extend the base definition (which it did as you can see on line 1). Unfortunately you have also @extended #sidebar .signup .standard_button and #registration .standard_button. Sass picks up that they are both definitions of .standard_button, it just happens that they have more specific selectors.

When I realised Sass handled @extends this way, it was one of those moments of satisfaction that comes with understanding a tool a little more. I’m not convinced I like that @extends work like this but that’s how it is and it’s better to know exactly how it works. To me, I think it would be better that when you extend a selector, you only extend that specific selector. For example, if I wanted to @extend the final two code blocks in the example above, I would have used @extend #sidebar .signup .standard_button; or @extend #registration .standard_button;.

If you’re thinking that doesn’t look too bad, keep in mind that the above is just a small example. If you’re working on a decent size site then the problems really balloons. Any time you @extend a class declaration, two additional sets of selectors are added to any selectors that feature that class declaration. So in the example above, if we added 5 extra @extends .standard_button; we would add an additional 10 selector lists to both of the others! It really can add up.

@extend placeholders for CSS Zen

The best way to avoid these problems is to use placeholder selectors in your @extends rather than using class selectors. The crux of the problem above is that you’re extending a class selector that may appear multiple times in your Sass file. To remove the problem we need to @extend something that only occurs once and placeholders allow us to do just that. Let’s rework the previous example, and use placeholders instead (note %standard_button on line 1 and @extend %standard_button on line 5):


.standard_button, %standard_button {
  display: block; padding: 10px; background: green; // Other cool styles
}

#sidebar .signup .standard_button { margin-top: 22px; }

#registration standard_button { margin-bottom: 33px; }

.article a { @extend %standard_button; }

When compiled, the CSS we generate is more in keeping with what we were trying to achieve:


.standard_button, .article a {
  display: block;
  padding: 10px;
  background: green;
}

#sidebar .signup .standard_button {
  margin-top: 22px;
}

#registration .standard_button {
  margin-bottom: 33px;
}

As a rule of thumb, it’s better to never extend a class selector and always to use a placeholder selector in it’s place instead. You can define the placeholder right at the same time you define your normal class selector, it keeps your Sass nice and readable. In the Sass reference, placeholders are described as being useful when you “run the risk of colliding with other classes that are being used in the HTML”. Why run the risk, use placeholders instead.

Wrap up

The examples in this article are basic (and perhaps almost trivial) but hopefully they make their point. What may seem simple can lead to a lot of confusion down the line, especially as your site and CSS grows. Ideally you want your CSS selectors to be specific and concise, which in turn results in CSS which is easy to read and to work with. Just as importantly, concise CSS leads to a smaller file sizes, which is vital for our mobile browsing buddies. There is also the funny (but surprisingly worrying) case of IE’s selector intolerance in version 8 and below which sets a maximum of 4095 selectors in any stylesheet.

There are nuances to how you choose to use @extends and perhaps you will come across an example of where you want to @extend selectors that are defined multiple times throughout your Sass. Either way keep an eye on your generated CSS because that at the end of the day is the final product.

If you liked this article then why not subscribe to our RSS feed.

Author: Chris Lamb

Chris is terrible at coming up with taglines.

Comments

Showing 9 Comments

Leave a comment
  1. [...] styles. It is like a variable on speed – since it’s more than just a string – but be careful about the [...]

  2. Isaac 29 May 2013 at 1:52 pm

    Incredibly helpful post. Having just begun to dig a bit deeper than the surface of SASS’s potential (i.e. beyond variables and mixins), this morning has been full of light-bulb “aha!”s as I learn about the @extend feature. And had I only relied on the official SASS documentation, I would have fallen right into the trap of bloating my compiled stylesheet due to using selectors instead of placeholders.

    Very helpful insight!

  3. Wendy 04 Jul 2013 at 4:51 am

    Thanks so much for this article. I’ve been using @extend with the same expectations as you had originally. Took a look at my css – freaked out a bit but wasn’t sure how to fix it so I just went with it. Glad I saw this post so I can help fix my issues. :)

  4. shaun 25 Jul 2013 at 5:01 am

    I thought I fully understood @extend, but apparently there is more to it. Thanks

  5. Paul d'Aoust 26 Jul 2013 at 5:46 pm

    Good thoughts. I’ve discovered another way @extend can really bloat your CSS, only this time it uses placeholders. Consider the following fictional Sass files:

    _toolbox.scss, which has useful placeholders like %clearfix, %media, %float-left, and %inline-block

    _grid.scss, which @imports _toolbox.scss for %clearfix

    _layout.scss, which @imports _toolbox.scss for %clearfix and a few others

    and finally, style.scss, which @imports _grid.scss and _layout.scss

    What happens when you compile style.scss? All your selectors that rely on %clearfix and the others get output in a single selector because of _grid.scss, which is nice and tidy as it should be. But then _layout.scss comes along and @imports _toolbox.scss, and guess what? Sass goes, ‘okay, you’re importing those placeholders again? Let’s re-declare everything that previously used those placeholders, plus all the new selectors that use them.’ There goes your nice tidy file size, cowboy; it’s just as bad as using mixins.

    The solution? I don’t know. You could have a global _import.scss file that imports everything, but that prevents you from only using bits and pieces of your mammoth library in any given stylesheet.

    • Dre 17 Jan 2014 at 1:16 pm

      Our solution is to keep all placeholders in a single _placeholders.scss, which is imported fairly early (just after our _mixins.scss) so that we can use it in subsequent partials. Our subsequent grid and layout files can merrily use the placeholders without issues.

  6. [...] with SassSass’ @extend feature is great for applying different typographic styles but be careful about the output.Using %placeholder instead of .class makes sure that the placeholder isn’t [...]

  7. Hannes 07 Apr 2014 at 9:53 am

    Tx for the great tip! I wasn’t paying attention to this problem before. And it has been bloating my css too.

Leave a Reply