How to give your Sass skills a boost

Jackie Balzer explores the computer science concepts that can supercharge your programming skills.

Sass is popular for making CSS quicker and easier to write, but did you know that it is a powerful scripting language too? Because Sass itself is written in the Ruby programming language, many of the same programming concepts that can be used in Ruby can be used in Sass.

Variables, logic, control directives and functions are all at your disposal and can be used to great effect in programming your CSS. What's more, frameworks such as Compass offer tons of additional functionality that can be used to take your CSS programming to the next level.

Variables

All popular CSS preprocessors have variables, but Sass stands apart with its multitude of data types. These determine the kind of value a variable can hold and what operations can be performed on it. Having specific data types is useful in many ways, and each data type serves a different purpose.

Sass shares several basic data types with many programming languages: numbers (integers or decimal numbers, optionally including a unit such as px or deg), booleans (true or false), strings (such as the word 'hello') and nulls (you can think of this as an empty value).

There are also some Sass-specific data types: colours (all CSS representations of a colour, such as blue or #0000ff), lists and maps. All data types support equality operators ( == , != , > , >= , < and <= ) and basic operations, such as arithmetic:

$inner-height: 100px - 30px; // equals 70px
$mix-color: #005cff+#202020; // equals #207cff

You can check if values are equal:

$test1: 16 != blue; // true
$test2: 30% > 100%; // false
$test3: blue == #0000ff; // true
$test4: 3/4 <= .25;  // false

Some data types have type-specific Sass functions:

$str-length: str-length('hello'); // gets the number of characters in a string: 5
$length: length(5px 10px 10px 5px); // gets the number of items in a list: 4
$round: round(4/3); // rounds the value of 4/3 to the nearest whole number: 1

You can change values from one type to another, which is called 'typecasting'. This can sometimes have unexpected results, as demonstrated in the code below:

$number: 1.2; // number data type
$unit: em; // string data type, since there is no associated number
$result: $number + $unit; // adds a number type and a string type, creating the string '1.2em'
$rounded: round($result); // causes an error - $rounded: "1.2em" is not a number for `round'

These basic operations are the foundation for how powerful Sass variables are.

Lists and maps

Lists and maps are other more complex data types. Lists are a series of values, like the ones we have already covered, or even other lists. Lists can be separated by spaces or commas, like so:

$margins: 10px 5px 20px 15px; // space-separated list
$font-stack: Helvetica, Arial, sans-serif; // comma-separated list
$colors: heading red, paragraph blue, figure orange, caption white; // comma-separated list of space-separated lists

A map is like a list on steroids: you can think of it as a list of key-value pairs. Unlike lists, maps must always be comma-separated. Like lists, you can use any data type for either the key or value, including other maps.

A simple map could look like this:

$map: ( heading: red,
             paragraph: blue,
             figure: orange,
             caption: white );

Though you can use all of the basic operations we have already covered on list and maps, their power lies within Sass functions, which you can use to iterate over, manipulate and retrieve data from your lists and maps.

Functions and control structures

Logic, control structures and functions can give you control over the flow of your code. The @if statement lets you control what happens if a condition is true or false. @for, @each and @while loops enable you to iterate over lists or maps.

Functions accept input and return a value, which are useful for repeated calculations. Sass has a ton of great functions built in, and you can write your own. Functions are awesomely flexible as they can take a specific number of arguments, named arguments or infinite arguments (netm.ag/arguments-258).

Text colour

Let's say we want to change our text colour based on how dark our background colour is:

@if(lightness($background) < 50%) {
  color: white;
} @else {
  color: black;
}

Sass' lightness function returns the lightness component of our background, and our equality < operator checks if its value is less than 50 per cent. If so, our text becomes white, otherwise, our text is black. There is an if function to shorten this:

$color: if(lightness($background) < 50%, white, black);

This function behaves like a a ternary operator, a common programming structure. It returns one of two values (the second or third argument) based on if the boolean condition (the first argument) is true or false. How about if you wanted to create a gradient with a specific set of colours? This is easy if you have a list:

$colors: #b00b00 #bada55 #de1e7e #f0feaf #ac1d1c #e1e100 #facade;
html {
  @include background-image(linear-gradient(top, $colors));
}

By default, each colour blends into the next. To make a gradient with a hard stop between the colours, each colour must have a stop at two points. This can be done with a loop:

$stops: ();
$list-length: length($colors);
@for $i from 1 through $list-length {
  $color: nth($colors, $i);
  $new-stops: $color percentage(($i - 1)/$list-length), $color percentage($i/$list-length);
  $stops: join($stops, $new-stops, comma);
} 
html {
  @include background-image(linear-gradient(top, $stops));
}

First, initalise a new empty list called $stops which will store the colour-percentage pairs. The @for loop iterates over the list of colours, counting from one through the length of the list. Sass' list function, nth, gets the colour at index $i of the list.

The percentage function calculates the percentage of the current ($i) and previous index's position in the list ($i - 1). Finally, the join function adds our new colour-percentage pairs to our list of $stops.

The resulting gradient looks like this:

linear-gradient(to bottom, #b00b00 0%, #b00b00 14.28571%, #bada55 14.28571%, #bada55 28.57143%, #de1e7e 28.57143%, #de1e7e 42.85714%, #f0feaf 42.85714%, #f0feaf 57.14286%, #ac1d1c 57.14286%, #ac1d1c 71.42857%, #e1e100 71.42857%, #e1e100 85.71429%, #facade 85.71429%, #facade 100%)

Recursion

Let's take it in step futher by using a recursive function instead of a loop. In computer science, recursion is a technique where the solution to a problem depends on solutions to smaller instances of the same problem, as opposed to iteration (looping).

This function can replace our @for loop:

@function gradient($stops: ()) {
  $stops-length: round(length($stops) / 2);
  @if $stops-length == $list-length {
    @return $stops;
  }
  $i: if($stops-length == 0, 1, $stops-length+1);
  $color: nth($colors, $i);
  $new-stops: $color percentage(($i - 1)/$list-length), $color percentage($i/$list-length);
  $stops: join($stops, $new-stops, comma);
  @return gradient($stops);
}

The function is bigger than the loop but is more self-sufficient: it initialises its own list of empty $stops, adding to it and passing it to itself on every call. It uses half the length of $stops to determine which $color in the list to calculate, and ceases calling itself when $stops is twice as long as $colors. Then it returns the list of $stops, which gets passed to linear-gradient().

This example does not necessarily save computational time, but it is an interesting look at how recursion can be used to solve a problem in a different way.

Conclusion

As you can see, Sass' usefulness extends far beyond the basics. It provides several different kinds of data types which you can use to manipulate values in interesting ways, use logic to output code in different ways, and control structures and functions to do advanced processing. The ideas covered in this article only just scratch the surface of the practical things you can do with Sass.

Words: Jackie Balzer

Jackie Balzer is an expert in Sass, CSS, HTML and JavaScript. This article first appeared in net magazine issue 258.

Like this? Read these!