Meet the variable’s big brother
Variables are great for values that you repeat over and over again. Things like colors, font sizes, margin amounts, etc. But what if you find yourself typing the same property:value
pairs over and over again? We’re using text-shadows all over the place:
.heading__header {
text-shadow: 0.55rem 0.55rem #fff;
}
.form__heading {
text-shadow: 0.55rem 0.55rem #15DEA5;
}
.about__heading {
text-shadow: 0.55rem 0.55rem #15DEA5;
}
.project__heading {
text-shadow: 0.55rem 0.55rem #15DEA5;
}
If you could cram all of that into some sort of variable, that would make things a whole lot less tedious, right?
$heading-shadow: text-shadow: 0.55rem 0.55rem #15DEA5;
Unfortunately, variables can only store values. 😓 Don't worry! Sass has given the variable a big brother, and he’s perfect for the situation. I give you the Sass @mixin!
Mixin it up
Instead being limited to values, mixins store entire blocks of code.🤘 You can use them to store CSS rule sets that you use frequently, much like a variable:
@mixin mixin-name {
css-property: value;
}
It looks a lot like writing a standard CSS block, right? Well, except for that name.
Again, it’s better to avoid being overly specific in when naming, instead referring to the mixin’s purpose when you label it. Since all of the elements that are receiving a text-shadow property are headings, you should name it heading-shadow
:
@mixin heading-shadow
From here on out, it’s identical to writing a normal CSS block: Use curly braces to wrap whatever you want in your ruleset, which, in the case of the heading-shadow
mixin, is the text-shadow property and its parameters:
@mixin heading-shadow{
text-shadow: .55rem .55rem #15DEA5;
}
And there you have your first Sass mixin. However, much like variables,heading-shadow
doesn’t really do anything until you instantiate it. To put your new mixin to work, you need to include it the CSS block where you want the shadow added.
In this case, we want to add the shadow to our .form__heading
selector, which we’ll do by typing @include followed by a space and the name of our mixin:
.form {
&__heading {
@include heading-shadow;
}
}
And when Sass compiles it to CSS, it places the ruleset from the mixin directly into the block that we @included it in:
.form__heading {
text-shadow: .55rem .55rem #15DEA5;
}
And let’s replace the other duplicates of the text-shadows while we’re at it:
.heading{
&__header {
text-shadow: 0.55rem 0.55rem #fff;
}
}
.form{
&__heading {
@include heading-shadow;
}
}
.about{
&__heading {
@include heading-shadow;
}
}
.project{
&__heading{
@include heading-shadow;
}
}
Boom! It’s raining text-shadows up in here:
.heading__header {
text-shadow: 0.55rem 0.55rem #fff;
}
.form__heading {
text-shadow: .55rem .55rem #15DEA5;
}
.about__heading {
text-shadow: .55rem .55rem #15DEA5;
}
.project__heading {
text-shadow: .55rem .55rem #15DEA5;
}
Try it out for yourself!
We’re using borders all over our site. They all have the same thickness, a solid line, and, to date, the same color. Rather than having to type all of that out, let’s create a @mixin
for it in this interactive exercise. This will save us some typing in the long run, and let us update the look of the border globally by updating the contents of the mixin.
Create a mixin called
border
using the@mixin
keywordAdd a border property to it, with a thickness of
.1rem
, a solid line, and a color of$color-primary
.Replace instances of the border property in our code base with our new
border
mixin, using the@include
keyword.Review the rendered HTML ensuring that things still look the same
Review the CSS to see that the Sass replaced instances of the mixin with it’s defined contents:
border: .1rem solid #15DEA5;
Check your answer with this CodePen!
Getting argumentative
Now you’ve replaced all of the text-shadows with your mixin. Well, not quite. There’s one text-shadow remaining; the heading in your header which is also using a text-shadow, but the color doesn’t match the one in your heading-shadow
mixin, so you can’t use it; at least not yet, that is.
.heading{
&__header {
// This shadow has a white colour, not mint!
text-shadow: 0.55rem 0.55rem #fff;
}
}
Making an entirely new mixin for a single instance isn’t worth it. Work smarter, not harder, remember? Instead, you can tweak your mixin, so that it will behave differently based on its inputs.
Inputs you say? 🤔
Why yes! If you place a set of parentheses after the name of your mixin, but before the curly braces, and fill the parentheses with a argument (or several), your mixin becomes customizable:
@mixin heading-shadow($colour){
text-shadow: .55rem .55rem #15DEA5;
}
$colour
looks a lot like a variable, right? That’s the argument. Think of arguments as empty variables that only live within the mixin. You set their value each time you include the mixin in your code, and that value is used within the mixin block when it is compiled to CSS:
@mixin heading-shadow($colour){
text-shadow: .55rem .55rem $colour;
}
Sass will now fill the $colour
variable within the mixin with the supplied color value, creating a shadow with the desired color for the heading header. Try typing that 10 times, fast!
.heading{
&__header {
@include heading-shadow(#fff);
}
}
And when you check out the compiled CSS code, the heading header has a white shadow!
.heading__header {
text-shadow: 0.55rem 0.55rem #fff;
}
By default
Now you can make text-shadows in whatever color your heart desires. But, really, you only needed to customize the color once. For every other instance, the original $colour-primary
would have served just fine.
Rather than having to enter a color value each time you use your heading-shadow
mixin, you can set the default value for the argument. Then, if you decide not to customize the shadow's color, it will use the default instead. Do this by defining its value just like a normally declared variable:
@mixin heading-shadow($colour: $colour-primary){
text-shadow: .55rem .55rem $colour;
}
Now, if you omit the argument and don’t set a color value when you include it, Sass will assume that you want the shadow to be the default color, $colour-primary
.
If the default value for $colour
hadn't been set, you would have to go back and add in the arguments to all of the other inclusions of heading-shadow
. But now you don't need to worry about that, and can leave them as they are:
.heading{
&__header {
@include heading-shadow($colour-white);
}
}
.form{
&__heading {
@include heading-shadow;
}
}
.about{
&__heading {
@include heading-shadow;
}
}
.project{
&__heading{
@include heading-shadow;
}
}
Which compiles to:
.heading__header {
text-shadow: 0.55rem 0.55rem #fff;
}
.form__heading {
text-shadow: 0.55rem 0.55rem #15DEA5;
}
.about__heading {
text-shadow: 0.55rem 0.55rem #15DEA5;
}
.project__heading {
text-shadow: 0.55rem 0.55rem #15DEA5;
}
Perfect! You've transformed your heading-shadow
mixin into a flexible block of code that adapts to your codebase needs.
Try it out for yourself!
We’ve made a mixin for our border, which is cool. But we can only use it on our mint-green buttons. But we made those pink buttons as well, whose borders we still have to style the old fashioned way. gasp!!!
In this interactive exercise, let’s adapt our border mixin to accept an argument named $color
, allowing us to make it any color we want. And, since we are mainly creating mint-green buttons, let’s give the argument a default value, sparing us from having to enter an argument for the majority of our uses.
Add a
$color
argument to the border mixinUpdate the border property to use the
$color
value we pass it, rather than$color-primary
Add a default value to the
$color
argument of$color-primary
Replace the border property located with the
.btn--outline.btn--pink
selector with theborder
mixinPass
$color-secondary
as its argument
Review the rendered HTML, confirming that is hasn’t changed, visually, and check your answer with this CodePen.
Circling the wagons
Your mixin is looking good. You can fully customize the color as needed, or leave it be, and have it use a default value instead. But there are a few things you could do to take it up a level. Ideally, color and size values should be contained in variables, making them easy to locate and update as necessary. And yet the mixin is using hard-coded values for the size of the shadow offsets. Let's remedy that by declaring a $heading-shadow-size
variable:
$heading-shadow-size: 0.55rem;
But, rather than simply replacing the offset sizes within the mixin, let’s implement a $shadow-size
argument, and set $heading-shadow-size
as the default:
$heading-shadow-size: 0.55rem;
@mixin heading-shadow($colour: $colour-primary, $size: $heading-shadow-size;){
text-shadow: $size $size $colour;
}
Now the mixin is looking really solid. It’s maintainable thanks to variables and has a predictable, but customizable output due to the implementation of arguments and default values.
Coming up next, we’ll take a look at another of Sass's tools to manage duplicate modules of code: The extension.
Let's recap!
Mixins are a lot like variables, but instead of only storing values, they store entire blocks of code.
Using the @mixin keyword declares a mixin.
Using the @include keyword places an instance of the mixin with your code.
When Sass compiles the mixin instances, it replaces the mixin with its contents.
To make mixins more adaptable and reusable, you can include arguments when you declare them.
Arguments can change the output of the compiled code, such as defining custom colors or sizes.