Section 3: Intermediate Sass

Overview

In this section we're going to be covering some of the more powerful features of Sass. So far you've learned enough to help your current process some, but there hasn't been anything that's too overwhelmingly awesome. That's about to change!

On this page, we'll be covering the following:

  • Part 1: Making your Sass modular with partials.
  • Part 2: Using @extend to inherit from already created rulesets.
  • Part 3: Using @mixins to create reusable code.
  • Part 4: Stepping things up with @functions.

Part 1: Partials

Whats a _partial anyways?

So far, to style a new page that we've created, we've had to create an entirely new .scss file and type in all of the styles that we want to apply to that given page. While this has worked so far, what happens if we have a set of styles that we want applied to every page in our site? As you might imagine, copying and pasting from file to file violates the DRY principle. Using Sass partials is our golden ticket!

A Sass partial file is any file preceeded by an underscore. An example file name might look like _someFile.scss. The benefit of Sass partials is that they are not compiled by themselves, but instead are compiled only when you @include them in another .scss file. Lets look at an example of this and then talk about whats going on:

Example Code

// _demo-partial.scss
// Site wide variables
$site-wide-color: rgb(207, 100, 154)
$important-fonts: Arial, Times New Roman, Lucidia Console;

// Here are some classes I want accessible everywhere
.important-class {
  background-color: $site-wide-color;
  font-family: $important-fonts;
}
.bold-class {
  font-weight: bold;
  font-size: large;
}

// demo4.scss
// import _demo-partial.scss
@import 'demo-partial';
                                        
// use some variables from the partial file
#header-text {
    color: $site-wide-color;
    font-family: $important-fonts;
}
// demo4.css
.important-class {
background-color: #cf649a;
  font-family: Arial, Times, sans-serif;
}

.bold-class {
  font-weight: bold;
  font-size: large; 
}

#header-text {
  color: #cf649a;
  font-family: Arial, Times, sans-serif; 
}

Explanation

So lets take a look at what's going on here. The first thing I did was create a partial file called _demo-partial.scss. Aside from the variable declarations on lines 2-3, there's nothing too crazy going on there. I then created a file called demo4.scss, and thats where the fun really started! Line 2 of this file introduces something that's special about how Sass handles @import compared to plain CSS. Sass allows you to @import the name of the file without the leading underscore and the file extension, but thats not all that's going on.

Normally when you import files in a CSS document, the browser generates an HTTP request for each @import you use. Sass changes this by precompiling the contents of the partial file before generating the final CSS document. In doing so, you end up with only one HTTP request for a single stylesheet instead of multiple requests. This becomes even more handy when your @imported files also @import other files.

You should also notice that @importing _demo-partial gave me access to any variables that were declared in that file. On lines 6 and 7 of demo4.scss, I used $site-wide-color and $important-fonts, which had been declared in the partial file. The advantage you gain from this method of handling @imports is that it makes managing your stylesheets more modular! Want to change the color of $site-wide-color everywhere it's used? Just update _demo-partial and you're good! Partials make it really easy to write small snippets of Sass and then @import that wherever and whenever you need it!

Part 2: @extend and Inheritance

@extend where?

Now that we've got an idea of how to @import code from external files, the next logical step would be to consider how we can reuse the code we've already written in our current file. If we rephrase the question, we might ask: How can we allow our new rules to inherit from other rulesets we've already created? This is exactly what the purpose of the @extend operator is! Before we get ahead of ourselves, let's look at some code:

Example Code

// demo5.scss
@import 'demo-partial';

.bold-and-underline {
    @extend .bold-class;
    text-decoration: underline;
}

.bold-underline-border {
    @extend .bold-and-underline;
     border: 1px solid black;
}

%i-shouldnt-show-up-in-the-css-file {
    background: blue;
}

%i-should-be-in-css-file {
    @extend .important-class;
}

#important-thing {
    @extend %i-should-be-in-css-file;
}
// demo5.css
.important-class, #important-thing {
  background-color: #cf649a;
  font-family: Arial, Times, sans-serif; 
}

.bold-class, .bold-and-underline, .bold-underline-border {
  font-weight: bold;
  font-size: large; 
}

.bold-and-underline, .bold-underline-border {
  text-decoration: underline; 
}

.bold-underline-border {
  border: 1px solid black; 
}

Explanation

First thing's first, on line 1 of demo5.scss, I've included the partial _demo-partial.scss so we have access to all the goodness defined therein. Next, on line 4 you should see the first new thing: @extend .bold-class; What you're seeing here is Sass's way of telling one ruleset to inherit from another. If you look at lines 6 and 11 in the compiled css (demo5.css), you should see that .bold-and-underline occurs on both. Also, you should see that the rules from .bold-class (defined in _demo-partial.scss) are included in the compiled output. This means that you as the developer only need to apply the one class to an element you want to style instead of applying multiuple classes to achieve the same result. You shold also pay attention to line 9 of demo5.scss. Sass allows you to use nested @extends, meaning you can @extend a ruleset that @extends another ruleset.

Now lets take a look at lines 13-23. You should notice on lines 13 and 17 that the two defined rulesets are preceeded by the percent symbol. That is Sass's way of creating what are called Extend Only Rulesets. Any extend only rulesets that are @extended in your .scss files are automatically compiled and included, and any that you don't use, are left in your .scss source. In our case, you should notice that #important-thing gets styled with the rules from .important-class because %i-should-be-in-css-file @extends .important-class. Writing your code using @extends helps to cut down on clutter and allows you as the developer to create rulesets that give you greater flexibility.

Part 3: Writing Reusable @mixins

Who'se @mixin what?

Our next topic of discussion will be that of the @mixin ("mix in"). @mixins are similar to inheritance and @extends in that you are using a pre-existing set of rules. The difference is that you are not inheriting from some other selector but are instead, adding a set of styles to a new ruleset in your CSS. @mixins end up creating new rulesets in the compiled CSS files and so, should be used resonsibly to avoid cluttering your compiled stylesheet. Let's take a look at some code and then dig into the details!

Example Code

// demo6.scss
$list-size-definition: 200px, 20px;
$map-size-definition: (height: 300px, border-radius: 5px);

@mixin giant-strange-text {
    font-size: xx-large;
    font-style: oblique;
    font-weight: 900;
    font-family: fantasy;
}

@mixin custom-size-thing($height, $border-radius: 10px) {
    height: $height;
    width: $height * 2;
    border-radius: $border-radius;
}

#some-interesting-element {
    @include giant-strange-text;
}

#custom-element-1 {
    @include custom-size-thing(100px);
}

#custom-element-2 {
    @include custom-size-thing($list-size-definition...);
}

#custom-element-3 {
    @include custom-size-thing($map-size-definition...);
}
// demo6.css
#some-interesting-element {
  font-size: xx-large;
  font-style: oblique;
  font-weight: 900;
  font-family: fantasy; 
}

#custom-element-1 {
  height: 100px;
  width: 200px;
  border-radius: 10px; 
}

#custom-element-2 {
  height: 200px;
  width: 400px;
  border-radius: 20px; 
}

#custom-element-3 {
  height: 300px;
  width: 600px;
  border-radius: 5px; 
}

Explanation

So whats going on in the above code? The first things you should notice are the two @mixin declarations on lines 4 and 11. The general format for defining a mixin is as follows:


@mixin mixin-name[($argument1[, $argument2[: default-value]])] {
  property1: value1;
  property2: value2;
}

First you start with the @mixin directive, followed by the mixin name. You then, follow that with any optional arguments, and their optional default values. In the case of our two mixins, giant-strange-text takes no arguments and custom-size-thing takes arguments for the $height and the $border-radius of the thing being styled. Additionally, custom-size-thing specifies a default value for $border-radius if no value is provided. Lets isolate a few interesting lines from demo6.scss and talk about what's going on:


// demo6.scss
#some-interesting-element {
    @include giant-strange-text;
}

As you can see in the above line of code, we're' using the @include directive to access the styles that were defined in the giant-strange-text mixin. This is the most basic way to use mixins in your code. If you look at the generated output on lines 1-6 of demo6.css, you'll notice that all of the styles from giant-strange-text have been injected into the rule. This is exactly how @mixins work. Think about all the typing you've just saved!


// demo6.scss
#custom-element-1 {
    @include custom-size-thing(100px);
}

This example illustrates how to call @mixins that take arguments. because custom-size-thing provides a default argument for $border-radius, the only value we need to provide is for the $height argument. Once those arguments are passed into the body of the output, they are assigned directly to height and border-radius and then the results of the calculation, $height * 2 is assigned to the width. This illustrates the power of @mixins. Should you ever decide to change this height ratio later, all you'd need to do is update the @mixin definition and your whole site would be good to go!


// demo6.scss
#custom-element-2 {
    @include custom-size-thing($list-size-definition...);
}

#custom-element-3 {
    @include custom-size-thing($map-size-definition...);
}

Our final example here illustrates how we can pass @mixins lists and maps that contain the arguments that we want to be evaluated. In order to perform this though, we have to perform a little Sass magic by following the name of the list/map with an elipsis (...). This tells the Sass compiler to unpack the list/map and pass in the values in they order they appear. If you observe the output on lines 14-24 of demo6.css, you should notice that the CSS looks like we had typed in the arguments directly. That's how we know things are working!

Part 4: @functions FTW

How does a @function function?

For our final part of this section, we're going to take a brief look at Sass functions. If you have any other programming experience, then this should look very familiar to you. If you don't, then this may be tough to grasp, but don't be worried. Using functions is a more advanced feature of Sass and in reality, you probably won't be writing them on a daily basis unless you're writing Sass libraries. Lets look at some code!

Example Code

// demo7.scss
$some-random-value: 100px;

@function multiplyBy($value-to-multiply) {
    @return $value-to-multiply * $multiplier;
}

$multiplier: 10;
#totally-boring-element {
    height: multiplyBy(2px);
}

$multiplier: 3;
#a-crazy-cool-element {
    height: multiplyBy($some-random-value);
}
// demo7.css
#totally-boring-element {
  height: 20px; 
}

#a-crazy-cool-element {
  height: 300px; 
}

Explanation

The first thing you shold look at in the above code examples is the function definition that occurs on lines 3-5. The standard syntax for defining functions is as follows:

@function functionName([$argument1[, $argument2]]) {
  /* Do some work.  Use your (optional) arguments. */
  @return $something;
}

In our function multiplyBy, we're accepting a single input parameter called $value-to-multiply. Then, we're returning that value (presumably a number type) multiplied by a global variable called $multiplier. In every case, multiplier needs to be defined in the global scope and should have some value before calling multiplyBy. You don't always have to create functions that use global variables, but it is worth noting that you can do so if it pleases you. Let's now consider the two ways that we call our function:


$multiplier: 10;
#totally-boring-element {
    height: multiplyBy(2px);
}

In this examle, we're starting by defining our global multiplier with a value of 10. This means that any call to multiplyBy will result in our called value being multiplied by 10. As you can see on line 3 of demo7.css, the result of 2px * 10 is 20px.


$multiplier: 3;
#a-crazy-cool-element {
    height: multiplyBy($some-random-value);
}

This example demonstrates that we can call functions using pre-defined variables. In this case, we're using $some-random-value and multiplying that by the (recently redefined) $multiplier. On line 6 of demo7.css, you can see the resulting value of 300px.

Built in Functions

While beyond the scope of this how-to guide, it's worth noting that the vanilla Sass library has a large number of built in functions that you can use. Some of these build on functions that already exist in CSS and others add in new features. For more information, check out the official Sass function docs.

Wrap Up: You've come so far!

Congratulations! If you've made it this far, you've learned enough to really optimize your stylesheet writing prowess. At this point, you should be able to:

  • Use partials to make your code more modular and easy to maintain.
  • Inherit from other styles using the @extend directive.
  • Pre-define sets of styles using the @mixin directive.
  • Offload work to the Sass compiler by using the @function directive.

At this point, we've covered all the syntax that we're going to look at in terms of the Sass language itself. If you want to learn more about what Sass has to offer, please consider checking out @if statements, @for loops, @each loops, and @while loops.