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.
