Table of Contents
Learning features and tools of advanced Sass can help you write a better code. There is no doubt about it. Being able to work with these features can also increase your productivity. In his article, we will discuss exactly these features and tools. You will learn about some of the most powerful parts of Sass. What features do I mean? Did you ever wish to use functions and loops in CSS? Today, you will learn how to use features such as these to take your Sass skills to another level!
Function directives
The first feature that belongs to advanced Sass are function directives. If you though that mixins are cool and handy, function directives, or custom functions, will blow your mind. It is important to mention that mixins and functions can sometimes be used interchangeably. It can happen that you will find yourself in a situation where you can use both. Both, functions and mixins, accept parameters. And, they both start with “@” symbol. So, they can sometimes look similar.
Code:
// Simple function called foo without parameter @function foo() { @return (33px / 2); } // Function in use div { width: foo(); } // Simple function called foo with parameter @function foo($parameter) { @return ($parameter / 2); } // Function foo in use div { width: foo(150px); }
Functions vs mixins
The main difference between those two is that functions return values. Mixins, on the other hand, return block(s) of CSS code. This can make it easier for you to understand when to use which. For example, let’s say you are in a situation when you need something that returns a value. This is good use of custom functions, to avoid doing the same calculations over and over again. In that case, you don’t know for what CSS property will you use this value. So, mixin will not work.
Why? Mixin requires and returns block of valid CSS. Valid CSS requires property and value. In other words, mixin is not an option. You have to use function. In order to illustrate this better, I will give you more concrete example. One of the custom functions I use the most is function for converting pixels to rems. If you read some of my tutorials that included Sass, it is very likely I used my favorite remy function here and there.
This function is very simple. It takes takes two parameters, first is $value and it is necessary. The second one is called $base. This one is optional. It accepts only values in pixels. Then, it does simple calculation (division and multiplication) and returns a value in rems.
Code:
// Remy function for converting pixels to rems @function remy($value, $base: 16px) { @return ($value / $base) * 1rem; } // Remy function in use header { width: remy(360px); height: remy(150px); box-shadow: 0 remy(15px) remy(35px) rgba(50,50,93,.1), 0 remy(5px) remy(15px) rgba(0,0,0,.07); } h1 { font-size: remy(21px, 14px); }
CSS:
header { width: 22.5rem height: 9.375rem box-shadow: 0 0.9375rem 2.1875rem rgba(50, 50, 93, 0.1), 0 0.3125rem 0.9375rem rgba(0, 0, 0, 0.07); } h1 { font-size: 1.5rem; }
Functions plus mixins
One thing I have to mention is that you can use functions inside mixins. For example, you can create a simple mixin for typography. Then, you can use remy function inside it to convert all pixel values to rems. There is one thing you need to pay attention to. Make sure to declare the function before you use it inside the mixin.
Sass:
// Remy function for converting pixels to rems @function remy($value, $base: 16px) { @return ($value / $base) * 1rem; } // Mixin for typography @mixin typography($size-h1, $size-h2, $size-h3, $size-h4) { h1, h2, h3, h4 { font-weight: 700; color: #212121; } h1 { font-size: remy($size-h1); } h2 { font-size: remy($size-h2); } h3 { font-size: remy($size-h3); } h4 { font-size: remy($size-h4); } } // Typography mixin in use @include typography(24px, 21px, 18px, 16px);
CSS:
h1, h2, h3, h4 { font-weight: 700; color: #212121; } h1 { font-size: 1.5rem; } h2 { font-size: 1.3125rem; } h3 { font-size: 1.125rem; } h4 { font-size: 1rem; }
Sass and built-in functions
Let’s quickly talk about one interesting thing before we move to another topic of advanced Sass. When you install Sass on your computer, it already comes with a ton of useful built-in functions. It has functions for working with colors, opacity, strings, numbers, lists and maps. It also has functions for introspection, working with selectors and much much more. You can see the list of functions to get better in advanced Sass in official documentation of Sass.
Also, there are Sass frameworks or libraries such as Compass and Bourbon. These frameworks will add more tools to your toolkit of advanced Sass. You can choose from mixins and functions for typography, grids, prefixes, sprites and various helpers. The downside is that you need to install these libraries before you can use them. The upside? It will speedup your work. A lot of mixins and functions you need already exist. So, you can use those instead of creating them.
Control directives
Another handy feature of more advanced Sass are control directives. Now, you may ask what are control directives. Have you ever heard about terms such as if, for, each and while loop? If you have at least basic knowledge of JavaScript, you probably know many of them, if not all. In Sass, these tools exist as well. And, they work in the same way you are used to. The difference is that if statements and loops (for, each, while) are called control directives. As you will see, these directives can help you create more versatile and flexible mixins and functions.
If directive
Let’s start with @if directive. This directive accepts expression. Then, it returns blocks of CSS styles depending on whether the expression is true or false. Simply said, if this is true return this. Otherwise, return that. Just like in JavaScript, and other programming languages, you can also use @else if to cover more options. One thing to remember about is that @if directive doesn’t work with “strict equality” operator (===) you may know from JavaScript. Use only “loose equality” (==).
Sass:
// Simple mixin for flexbox @mixin flexbox($use-flex: true, $flex-direction: row) { // Define if directive for use of flexbox @if ($use-flex == true) {// the same as $use-flex == true display: flex; flex-direction: $flex-direction; flex-wrap: wrap; } @else { display: block; li { display: inline-block; } } } // Mixin for box-shadow @mixin box-shadow-helper($level) { @if $level == 1 { box-shadow: 0 1px 3px rgba(0,0,0,.12), 0 1px 2px rgba(0,0,0,.24); } @else if $level == 2 { box-shadow: 0 3px 6px rgba(0,0,0,.16), 0 3px 6px rgba(0,0,0,.23); } @else if $level == 3 { box-shadow: 0 10px 20px rgba(0,0,0,.19), 0 6px 6px rgba(0,0,0,.23); } } // Use mixin for flexbox with default parameters .nav-primary { @include flexbox; } // Use mixin for flexbox with $use-flex parameter set to false .nav-secondary { @include flexbox($use-flex: false); } // Mixin for box-shadow with different parameters .btn { @include box-shadow-helper(1); &:focus, &:hover { @include box-shadow-helper(2); } &:active { @include box-shadow-helper(3); } }
CSS:
.nav-primary { display: flex; flex-direction: row; flex-wrap: wrap; } nav-secondary { display: block; } .nav-secondary li { display: inline-block; } .btn { box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24); } .btn:focus, .btn:hover { box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23); } .btn:active { box-shadow: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23); }
For directive
Another directive for your advanced Sass toolkit is @for directive. This directive comes in two forms. The difference between these forms is in slightly different syntax. Well, the difference is just one keyword. The syntax of the first form is “@for $variable from $start through $end”. The syntax of the second form is “@for $variable from $start to $end”. Did you see it? It is about the “through/to” keywords. When you use through keyword, compiled CSS will include the end point.
Sass:
// For directive for generating 12-column grid @for $i from 1 through 12 { .col-#{$i} { width: 100% / 12 * $i; } }
CSS:
.col-1 { width: 8.33333%; } .col-2 { width: 16.66667%; } .col-3 { width: 25%; } .col-4 { width: 33.33333%; } .col-5 { width: 41.66667%; } .col-6 { width: 50%; } .col-7 { width: 58.33333%; } .col-8 { width: 66.66667%; } .col-9 { width: 75%; } .col-10 { width: 83.33333%; } .col-11 { width: 91.66667%; } .col-12 { width: 100%; }
As you can see, @for directive can be quite useful advanced Sass tool to generate custom grids. In the example above, we used the form, or variant, of @for directive with “through” keyword. Watch what will happen if we replace it with “to” keyword. The twelfth column will not be generated.
Sass:
// Use "to" instead of "through" @for $i from 1 to 12 { .col-#{$i} { width: 100% / 12 * $i; } }
CSS:
.col-1 { width: 8.33333%; } .col-2 { width: 16.66667%; } .col-3 { width: 25%; } .col-4 { width: 33.33333%; } .col-5 { width: 41.66667%; } .col-6 { width: 50%; } .col-7 { width: 58.33333%; } .col-8 { width: 66.66667%; } .col-9 { width: 75%; } .col-10 { width: 83.33333%; } .col-11 { width: 91.66667%; }
Each directive
The syntax of this advanced Sass directive is relatively similar to the syntax of @for directive. As every directive, it begins with the “@” symbol. Then, there is some $variable followed by “in” and some $list of items. In the whole, it looks like “@each $variable in $list”. When Sass runs through @each directive, it sets the $variable to each item in the list you provided. Let’s take a look at few simple examples to help you understand @each directive.
Sass:
// List of button color variants $btn-variants: blue green grey red yellow; // Each directive for generating buttons @each $btn in $btn-variants { .btn-#{$btn} { background-color: #{$btn}; } }
CSS:
.btn-blue { background-color: blue; } .btn-green { background-color: green; } .btn-grey { background-color: grey; } .btn-red { background-color: red; } .btn-yellow { background-color: yellow; }
We can also use @each directive to loop through a map of key / value pairs to generate variants of buttons with different sizes.
Sass:
// Define map of button variants and sizes $btn-sizes: ( tiny: 12px, small: 14px, default: 16px, large: 18px, ); // Create @each directive for generating buttons // Parameters: $btn-variant is for key of the pair (tiny, small, etc.), // $btn-size is for value of the pair (12px, 14px, etc.) @each $btn-variant, $btn-size in $btn-sizes { .btn-#{$btn-variant} { font-size: #{$btn-size}; } }
CSS:
.btn-tiny { font-size: 12px; } .btn-small { font-size: 14px; } .btn-default { font-size: 16px; } .btn-large { font-size: 18px; }
Let’s take this lesson from advanced Sass farther. We can create a mixin containing @each directive to generate basic styles for buttons.
Sass:
// Define map of button variants and sizes $btn-sizes: ( tiny: 12px, small: 14px, default: 16px, large: 18px, ); // Create mixin for generating button styles @mixin generate-btns() { .btn { display: inline-block; font-weight: 400; color: #fff; } // Create @each directive for generating buttons variants @each $btn-variant, $btn-size in $btn-sizes { .btn-#{$btn-variant} { font-size: #{$btn-size}; } } } // Generate buttons @include generate-btns();
CSS:
.btn { display: inline-block; font-weight: 400; color: #fff; } .btn-tiny { font-size: 12px; } .btn-small { font-size: 14px; } .btn-default { font-size: 16px; } .btn-large { font-size: 18px; }
While directive
The last of the advanced Sass controls directives is @while. This control directive works in a similar way as @if directive. It accepts some expression that is either true or false. Until this expression is false, it will repeat the loop you specified. From this point of view, we can say that @while directive is like @if after merging with @for. Or, that it could be a child of these two. Do you remember the mixin for typography we created in the beginning? We can use @while directive instead of mixin.
One thing. Just like in JavaScript or any other language, make sure to implement code that will change the expression to false at some point. Otherwise, you will end up with infinite loop.
Sass:
// Variables for @while directive $iterations: 1; $size: 29px; // Create @while directive for generating heading styles – h1 to h4 @while ($iterations < 5) { h#{$iterations} { font-size: $size - $iterations; } // Increase the value of $iterations variable $iterations: $iterations + 1; }
CSS:
h1 { font-size: 28px; } h2 { font-size: 27px; } h3 { font-size: 26px; } h4 { font-size: 25px; }
Choosing the right control directive (loop)
Let’s wrap this part of advanced Sass by exploring one interesting question. How to choose the right control directive (loop)? At this moment, we can choose from three options. Which loop is better for which use case or situation? If you know the range (from to) you need to stay in, use a @for loop. For example, when you need to generate a number of selectors (:nth-child or :nth-of-type) or a series of classes, use @for directive or loop.
And, for the rest of cases, you will probably have either some list or map of items. Then, it is usually more convenient to use @each directive. Yes, this means that you will not work with @while directive so often. I have to admit that I used @while directive only in two situations. First, when I was learning the techniques of advanced Sass. Second, when I was writing this tutorial on advanced Sass. Other than that, never. Still, it is good to at least know such a thing exists.
Closing thoughts on advanced Sass
As you can see, features of advanced Sass can enhance your CSS with powers known in many programming languages. Some of these features of more advanced Sass can be a bit harder to understand. If you feel it’s a bit complicated. Don’t worry, you are not alone. It took me some time to wrap my head around all of this. What helped me the most was practicing all of it on my own. Reading is just not enough. Do the work. Get your hands dirty. And soon, you will see the results.
One last thing. This is not the last article from this mini-series. There will be one more next week. In this article, we will talk about some additional handy features of advanced Sass. Also, we will go through a couple of best practices. My hope is that knowing this will not only help you use Sass in a better and more productive way. It will also help you write better and more maintainable code. So, definitely stay tuned for the last part. For now, have a great day!
If you liked this article, please subscribe so you don't miss any future post.
If you'd like to support me and this blog, you can become a patron, or you can buy me a coffee 🙂