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.