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.
Contents
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.