10 things you never knew you could do with Sass

Sass

While there are a number of other CSS preprocessors to choose from, Sass has long been the most popular option in the web community (don't know what we're talking about? Read What is Sass? first). It's feature-rich, stable and powerful. What's more, the massive community of users mean support and advice are top-notch. In this article, we offer 10 top tips for getting more from Sass. 

01. Nesting

The ability to mirror the nested hierarchy style of HTML in CSS is arguably the biggest improvement that Sass brings as a preprocessor to plain CSS. It stops you from repeating yourself constantly, makes project maintenance easier, and is far more pleasant to read as you can see exactly what rule relates to which selector:

// Plain CSS
.foo { .. }
.foo ul { .. }
.foo ul li { .. }
// Sass
.foo {
 ..
 ul {
 ..
 li { ..}
 }
}

02. Reference symbol

When nesting, you can use the & symbol to reference the parent element. This helps in two main ways. Firstly, you can neatly nest pseudo-states or elements:

.foo {
 background-color: red;
 &:hover { background-color: blue; }
 &:after { content: 'bar'; } 
}

... but also to combat pesky overwrite issues:

.foo {
 background-color: red; 
 &.bar { background-color: green; }
 .ie &.bar { background-color: yellow; } 
}
// =>
.foo { background-color: red;}
.foo.bar { background-color: green; } 
.ie .foo.bar { background-color: yellow; } 

03. Variables

All variables in Sass begin with a $ symbol followed by the name (without spaces):

$breakpoint-sm: 576px;

Which makes handling global values such as colours, fonts and breakpoints infinitely easier.

04. Object variables

In Sass, variables can also be stored as an object for cleanliness. Here is a small object of variables for breakpoints:

$grid-breakpoints: (
 sm: 576px,
 md: 768px,
 lg: 992px
);

In order to retrieve a variable stored in this way, you can use a  built-in Sass function called map-get to return one of the variables based on a key passed in as an argument. Here we'll use it to set the max-width value of a @media query:

@function breakpoint-var($breakpoint) {
   @return map-get($grid-breakpoints, $breakpoint);
}
@media (max-width: breakpoint-var(lg)) {
}
// => @media (max-width: 992px) {
// 
// }

CSS does now have the var() function to be able to create simple variables with -- prepended, but the Sass preprocessor version is much more powerful.

05. Mixins

Mixins bring the power of reusable code to CSS. Rather than having to go throughout a stylesheet and change a property multiple times, you can just handily change it inside a mixin:

@mixin foo() {
 border: 1px solid red;
}
.bar {
 @include foo();
}
// => 
// .bar { border: 1px solid red; }

You can also set parameters for your mixins so that you can use the same styles but with slight adjustments based on your needs. You can even set default parameters for those arguments:

@mixin foo($width: 1px, $color: red) {
 border: 1px solid red;
}
.bar {
 @include foo();
}
.bax {
 @include foo(5px, blue);
}
// => 
// .bar { border: 1px solid red; }
// .baz { border: 5px solid blue; }

06. Functions

Functions and mixins can often be interchangeable and accomplish the same result, but their purposes are slightly different. As with many things in programming, the answer is use logic: mixins are used more for includes, and functions are more for returning values. For example, the breakpoint-var() function we used earlier has a return not seen in mixins.

07. @extends

This is brilliant for reducing duplication in your CSS, allowing classes to share a set of properties with one another.

.foo {
 color: black;
 border: 1px solid black;
}
.bar {
 @extend .foo;
 background-color: red;
}
// =>
.foo, .bar {
 color: black;
 border: 1px solid black;
}
.bar { background-color: red; }

You can even extend multiple selectors in the same rule using a comma-separated list:

@extend .foo, .bar;

It can also be great for simplifying the way you name classes:

// Without @extend
.foo {
 background-color: red;
 border: 1px solid blue;
 color: black;
}
.bar { background-color: green; }
<div class="foo bar">...</div>
// With @extend
.foo {
 background-color: red;
 border: 1px solid blue;
 color: black;
}
.bar {
 @extend .foo;
 background-color: green;
}
<div class="bar">...</div>

The only downside to this we would raise is the drastic increase in the size of your stylesheet if you use it constantly.

08. @import

There's nothing more intimidating or headache-inducing than an overly large stylesheet, particularly if you're scouring through it for a particular part. Thankfully Sass allows you to separate your styles into multiple files, and then use @import to bring then in as and when you need them! The typical naming convention for a partial is prepending an underscore before the file name and then importing it with its name. 

For example, if we wanted to import separate styles for buttons, we would name the file _buttons.scss and then add: @import buttons;. Just like @extend, you can import multiple imports using a comma-separated list:

@import 'buttons', 'forms';

You can import inside a selector – if it makes sense based on the contents of your partial:

.foo {
 @import 'bar';
}

And your partials don't even have to be on the same directory level:

@import 'utils/mixins';
.foo {
 @import 'components/buttons';
}

This gives you complete freedom in architecting the layout of your styles.

09. Looping

You can loop in Sass using three main rules:

  • @for – Loop for a set amount of iterations, with access to the index on each loop
  • @while – Loop until the check in place is no longer true
  • @each – Loop through every item in a given list

If you have experience with JavaScript (or most programming languages) you will have been exposed on some level to these principles and they can be just as powerful in your styling as they are there. 

For example, Bootstrap's entire flex-based grid system is built in Sass using all of these heavily. A simpler example could be looping five times to quickly created a staggered fade-in animation for items using nth-child:

.foo {
 @for $i from 1 through 5 {
 &:nth-child( #{i} ) {
 opacity: 1;
 transition: opacity .5s;
 transition-delay: #{($i * .05) + .05}s;
 } 
 }
}

10. Interpolation

The @each loop works really well with a powerful feature called interpolation; a way of using the content of the values you're looping (syntax is #{VALUE}) in the output itself. So, if we'd like to set up our heading rules using an object variable with keys and fonts, we could do the following:

$fonts: (
 h1: 24px,
 h2: 20px,
 h3: 16px
);
@each $element, $size in $fonts {
 #{$element} {
 font-size: $size;
 line-height: ($size * 1.5);
 }
}

This article was originally published in creative web design magazine Web Designer. Buy issue 276 or subscribe.

Read more: