LearnExtending Placeholder Selectors with Sass

Guil Hernandez
writes on April 15, 2013

I recently wrote an article on creating a themable button set with Sass where I used a mixin to define all base styles, then a color value was passed as an argument when included in each theme.

Example:

@mixin btn-theme($btn-color) { ... }

// Themes

.purple {
   @include btn-theme($purple);
}

The concept of mixins seems simple on the surface, but if we’re not careful, things can quickly get out of hand once our Sass starts to grow because they duplicate a lot of CSS. It’s essentially copying and pasting blocks of code.

Fortunately, in our case there’s really no duplicate code to be concerned about. Using a mixin for creating the themes is still an efficient approach––it generates a different output with every use because it’s dependent on the color argument.

View the full example in Codepen.

Things Can Be Better

I have other concerns about this approach:

  • Every button element relies on two HTML classes: the general base style class and the more specific theme class.
  • The theme classes are purely presentational.
  • A number of unused CSS rules are being outputted.
<button class="purple btn">Start!</button>

So there is plenty of room for improvement, especially by taking advantage of two handy Sass features: @extend and placeholder selectors.

The Sass @extend Feature

The @extend directive allows one selector to inherit the styles of another selector. It prevents code bloat by grouping selectors that share the same styles into one rule.

Here’s a basic example:

.square {
   width: 200px;
   height: 200px;
   background-color: lightcoral;
}
.circle {
   @extend .square;
   border-radius: 50%;
}

Will compile to:

.square, .circle {
  width: 200px;
  height: 200px;
  background-color: lightcoral;
}
.circle {
  border-radius: 50%;
}

It’s also possible for one selector to extend another selector that in turn extends a third:

.ellipse {
   @extend .circle;
   height: 120px;
}

Compiles to:

.square, .circle, .ellipse {
  width: 200px;
  height: 200px;
  background-color: lightcoral;
}
.circle, .ellipse {
  border-radius: 50%;
}
.ellipse {
  height: 120px;
}

To break it down simply: one class can have all the styles of another class, as well as its own specific styles. View this in CodePen.

Putting @extend to Work

@extend can benefit us because our buttons can inherit everything in the .btn style block without having to explicitly state it in the markup.

In our btn-theme mixin, let’s extend the .btn class:

@mixin btn-theme($btn-color) {
   ... 
   @extend .btn;
}

Now every .btn class can be removed from the markup because the theme class will always inherit its styles.

// Themes

.purple {
   @include btn-theme($purple);  
   /* btn-theme mixin now inherits .btn styles */
}
...

But we’re still left with the presentational classes in the markup––they don’t mean anything but arbitrary colors at this point.

<button class="purple">
   Button
</button>
<button class="blue">
   Button
</button>

The New “Silent Class”

Sass 3.2 introduced a special feature called placeholder selectors. They’re also referred to as “silent classes” because they won’t appear in our CSS output unless we @extend them.

It’s usually better to extend a placeholder selector instead of a class selector, so let’s make our .btn class a silent class. This way it won’t “exist” in our CSS output until we’re ready to use it.

A silent class is defined by using a % in front of the selector name:

%btn  {
   ...
}

We’ll also need to add the % when extended:

@mixin btn-theme($btn-color) {
   ... 
   @extend %btn;
}

This is an extremely useful feature because we can now create an entire library of theme colors, but they won’t get created (or outputted) until we call them.

Creating a Theme Library

Let’s create a new partial in the “scss” folder called “_themes.scss,” which we’ll need to import into “style.scss.”

@import 'themes';

In “_themes.scss” we can now create placeholder selectors of various color options––as many as we want. Check out the ones I created.

Examples:

%darkorange {
   @include btn-theme(#FF8C00);	
}
%darkslategray {
   @include btn-theme(#2F4F4F);	
}

Now we can give our HTML classes more meaningful names like:

<button class="btn-delete">
   Delete
</button>

<input type="submit" class="btn-submit">

If we need to change a color theme, we can reference one from our themes library by simply changing the extended silent class in _buttons.scss.

.btn-delete {
	@extend %firebrick;
}
.btn-submit {
	@extend %tomato;
}

And because they are silent classes, if any are reused, they will be properly extended in the CSS.

Conclusion

This is a more viable approach because we’ve created our solution entirely in the CSS and not the markup. I created a GitHub repo containing all the example code we discussed –– take a look.

Have you used @extend or placeholder selectors in your project? Let us know in the comments below.

10 Responses to “Extending Placeholder Selectors with Sass”

  1. Awesome, I love it, the final button code is so clean! I always disliked CSS because it always was a huge mess that was difficult to oversee. With SCSS styling finally becomes much cleaner, like I am used to from programming 🙂

  2. Mind.. blown. Thanks Guil! This is really helpful- as is your CSS Layout Techniques course..

    By the way, I just tried using this technique to extend the responsive / fluid grid system you layed out in that course.. And .. Drum Roll.. It’s working! I’m sure it can be refactored and cleaned up, but my goal was to be able to remove some of the unsemantic class names from the markup- although I don’t know how huge that benefit really is.. thoughts? Also, It would be great if perhaps you could expand upon the course or write a post showing how you would approach that challenge / best practices..

    Thanks again! Loving treehouse and learning a ton 🙂

  3. Mr Torcsy on July 15, 2013 at 1:13 am said:

    Great stuff Guil – much more efficient using %silent classes. My one gripe is that you cant @extend across files with SASS.

  4. That’s a pretty cool feature. I’ll try that out soon.

  5. Alfonse Surigao on June 5, 2013 at 2:40 pm said:

    Great article! I have just recently started using the silent classes in Sass and it has helped reduce my CSS file size and reduce my HTML markup as well, especially since I have created a whole class library for my own grid based framework.

  6. Dwayne R. Crooks on May 3, 2013 at 5:17 pm said:

    Is it bad practice to use a placeholder selector within a mixin. For e.g.

    @mixin boxes($w, $dir, $n: 10) {

    %base {
    float: $dir;
    }

    @for $i from 1 through $n {
    .box-#{$i} {
    @extend %base;
    width: $i * $w;
    }
    }

    }

    @include boxes(5px, left);

    I don’t see this example mixin being reused more than once to setup the boxes. But let’s say there was an example where the mixin would be called more than once. Then, I’m seeing that the base placeholder selector would be defined, so to speak, more than once. In my tests of this I’m not getting any errors. Thoughts?

    Also, base must occur within the mixin’s definition since it depends on one of its parameters.

  7. Courtland Santos on April 16, 2013 at 2:24 pm said:

    Thanks for the helpful tutorial. Is SASS much easier to learn than CSS? Are there video tutorials on treehouse that I can watch. I just completed the css foundations portion in the members area and would like to know more about SASS or even LESS. Thanks in advance.

    • You will need to learn CSS in order to work with SASS. SASS is essentially working with what you already learned in the CSS foundations course. With SASS you can go as in-depth with mixins, @extend directives or simply just use nested styles.

  8. Grumble Guy on April 15, 2013 at 11:25 pm said:

    Just am starting out in the SASS world, but this was good to see as an example going forward. Thanks!

Leave a Reply

You must be logged in to post a comment.

Want to learn more about CSS?

Learn how CSS allows you to apply visual styling to HTML elements with colors, fonts, layouts, and more.

Learn more