Sass – Understanding Stylesheet Scripting

Sass has come a long way in a short amount of time to offer web designers and developers a method of using scripting variables in their stylesheets, to change the apearance of their content.  Join with me as I dive into the world of Sass and learn more about CSS Preprocessors!  Lets go!

What Are Preprocessors?

Preprocessors are programs that take one type of data and transform it to another type of data.  In Sass, the preprocessor takes Sass code and transforms it to CSS.  The reason for Sass is it allows us to use variables and nesting and other features within our CSS.

With these features comes the most important reason for using a preprocessor, to save time.  By using Sass, we can accomplish more in less time.

Typical Sass Workflow

Write Sass Code > compile the code into CSS > load the CSS in the page

The compiling is an extra step, but the benefits outweigh the cost of using Sass.

Instaling Sass

Sass is a Ruby Gem, so you must first install Ruby.  If you are a Mac user, you already have Ruby installed.  Windows users should visit http://rubyinstaller.org/downloads/ and download the latest Ruby build.

Once downloaded, run the program and it will install Ruby on your system.  Now we need to install the Gem; open the Command Prompt with Ruby program (run as administrator) and use this line to install Sass:

gem install sass

That will install sass, the other thing to know is when to compile your Sass.  First of all, the sass files must be in a folder.  Then you will cd into the project folder and compile them using this command:

sass --watch fileName.scss:fileName.css

You are telling sass to watch for the .scss file in the folder (specified above) and compile the document into CSS.  You also set up a watch so whenever you change the scss, you also change the css

Of course, there are also third-party extensions to help with compiling, but I know you guys don’t like things the easy way 🙂

Sass Syntax

The Sass language has two syntaxes:

  1. SCSS (.scss) – New syntax
    1. scss is an extension of the css file.  Any valid .css file is also valid .scss, so you can just write plain css into your .scss file.  Can also incorporate newer variables and nesting behaviors into file
  2. SASS (.sass) – Older Syntax
    1. sass has many differences with css, but can still be compiled into css.  The main differences are semicolons are no longer used, replaced with line breaks to signal the end of a style declaration.  It also doesn’t use brackets, instead indentation is used to demonstrate style nesting, so:
      body{color:blue;} becomes body color:blue

Another difference is how you write comments.  In scss, can use CSS styles (/* */) but cannot use (//)

Decision rule is to use newer SCSS as it is closer to normal CSS, yet still as powerful as SASS

Nesting

Style nesting is a feature ommitted by classical CSS but embraced by Sass.  Nesting is basically writing one objects style inside the declaration for another element.  It can save a bunch of time if used correctly.

Example:

If you wanted to color anchor tags within a paragraph, you would use p a {color:example}; but in Sass, you can nest the a style within the p style, like so…


p {
    margin-bottom: 1.3em;
    color: #666;

    a {
        color: #589ED3;
    }
}

In the above example, the a style is located within the brackets of the p style, but when it compiles, it looks like…


p {
  margin-bottom: 1.3em;
  color: #666; }
  p a {
    color: #589ED3; }

So you can see that while the style is written differently, it compiles in the same way that you would expect.  Sass allows you to write less code, yet still compiles to be exactly the same as if you spelled it out.

Parent Selector (&)

For styling psuedo-elements (:hover, etc), you can use the & character as a placeholder for the root element you are styling.  So, if you wanted to style the hover state of a link…

Example:


a {
    text-decoration: none;

    &:hover {
        text-decoration: underline;
    }
}

Simply substituting the a with &, which fills the place of the parent tag. When compiled, it outputs as:


a {
  text-decoration: none; }
  a:hover {
    text-decoration: underline; }

Can also add classes or ids to the parent selector, so &.red above would select any a tags with a class or “red”.  It compiles to a.red, just as you would expect.

The parent selector can be used in a few different ways…

#demo {
    &-child { margin: .5em; }
    &child { margin: .5em; }
    &.child { margin: .5em; }
    & .child { margin: .5em; }
}

Compiles to become…

#demo-child {
  margin: .5em; }
#demochild {
  margin: .5em; }
#demo.child {
  margin: .5em; }
#demo .child {
  margin: .5em; }

The first example creates a new selector, adding the parent to the hyphen and then the word child (#demo-child).  The second example does the same, but no hyphen.  3rd example creates a class selector for the element, so the element has an id of demo and a class of child.  The final example selects an element with a dependent class of child within an element with an id of demo.

Can also put parent selector at different places in the style declaration to great effect:


.container {
    width: 90%;
    margin: 0 auto;

    .boxed-layout & {
        background-color: #fff;
        padding: 3em;
        border: 1px solid #eaeaea;
        border-width: 0 1px;
    }

    
}

This compiles to:

.container {
  width: 90%;
  margin: 0 auto; }
  .boxed-layout .container {
    background-color: #fff;
    padding: 3em;
    border: 1px solid #eaeaea;
    border-width: 0 1px; }
  

This would be used with javascript libraries such as Modernizr. Modernizr test the browser for its capabilities, then styles the page contents to reflect those abilities. With the above code, you could write the styles for different container elements in multiple different Modernizr versions.

Can also make complex selections:

.container {
    width: 90%;
    margin: 0 auto;

    > header {
        h1 {
            font-size: 4em;
            font-weight: 800;
        }
    }
}

Becomes…

.container {
  width: 90%;
  margin: 0 auto; }
  .container > header h1 {
    font-size: 4em;
    font-weight: 800; }

In the above example, we use the direct dependecy (>) to grab the header, then the h1 inside the header which is directly beneath the container element.

Important Considerations

Nesting is great, but improper nesting can get really complicated and hard to maintain as the pages grow.  Be careful how deep you are nesting.  As a general rule, dont go beyond 3 or maybe 4 DOM levels, and the final level has to be selecting psuedo-elements.

  • Nesting is BEST used for psuedo classes and elements.
  • When using parent selector, be careful to not create style documents that you cannot search through

 

Nested Properties

For style declarations that share the same namespace (font-family, font-size, font-weight), we can combine the declarations into one block:

font: {
        family: Helvetica, Arial, sans-serif;
        size: 1.125em;
        weight: 400;
    }

Compiles to:

 font-family: Helvetica, Arial, sans-serif;
  font-size: 1.125em;
  font-weight: 400;

Or in shorthand:

 font: 400 (font-weight) 1.125em/1.5 (font-size/line-height) Helvetica, Arial, sans-serif(font-family);

This works for anything that shares a property name (background, margin, padding, border, etc.)

Nesting the properties can create more code than necessary, however, it is good practice for beginners and helps make your SCSS file more readable.

Variables

One of the largest developments with the SASS language is the use of variables in your CSS document.  Variables represent reusable style values that can be given a namespace and called on any element, creating large and cascading changes with minimal typing.

Examples of Variables

Variables are a great method of styling repeated values without actually repeating each time.  They always begin with the $ (dollar sign or string) character, just as in JavaScript and are followed by a text string which indicates the purpose of the variable:

$text-color: #333

Then, for any styles which need a text color, you can simply replace the variable for the value, so:

body { color: #333;}

becomes

body { color: $text-color;}

Now the body will get the color value from the text-color variable.  This also means that we can simply change the text-color value and we will change the color values wherever we use the variable.

When compiled, SASS writes the correct CSS code (doesn’t use variables) for you!

Have a section at top of SCSS which sets many global properties for the document, then write my styles using these global properties to make things easier to manage and change going forward.

Most powerfully, variables can also perform operations.  Suppose this test case:
We can set a baseline text size with a variable, then compute different text sizes for the styles based on the baseline variable we have created.


$baseline: 12px;

We can set our text to that baseline by:


body {font-size:$baseline;}

But then we want to have our titles be twice as large as the baseline, we can compute that operation with SASS:


h1 {font-size:$baseline * 2;}

By multiplying the $baseline by 2, we get 24px, which we then style our H1 around. Of course, when the SCSS compiles it displays the 24px value, not the $baseline * 2 value.

Varialbe Scope

Just like JS, there is a scope in which the variables exist.  If you write all your variables at the top of the page, they are global and can be used by the entire stylesheet.  If your variables are placed inside of an elements style declaration, the variable is only available to that element or any children, but nothing above in scope.  However, if we put our varialbes inside style declarations, we can end the value with !global and allow the variable to be read by the entire document.

Variable Interpolation

Substitute a variable in an expression with its value

Lets say you have a variable with a value, for example:

$child: "widget";

then you wanted to add the value of the variable to a selector:

.sidebar-$child {

}

would not work, but you can use:

.sidebar-#{child} {

}

this compiles to:

.sidebar-widget {

}

This feature is also used when writing shorthand css:

font: 400 $font-size/$line-height $font-family

The 400 is font weight, and the rest of the variable could have values attached, however, the middle style of $font-size/$line-height would be calculated because / is an operator (division).  Instead, use interpolation to subsititute values correctly without conducting an operation

font: 400 #{$font-size}/#{$line-height} $font-family

Interpolation can also be used when using content styling:

p:before {content: "$content-before"} would need to be p:before {content: "#{$content-before}"}

Data Types Supported by SASS

SASS supports 7 main data types

  • Numbers
  • Strings (text)
  • Colors
  • Booleans
  • Nulls
  • Lists
  • Maps

Numbers

  • Numbers with units – margin: 100px; (100 is number and px is unit)
  • Decimal numbers without units – line-height: 1.5;
  • Whole Numbers without Units – font-wieght: 800;
  • Percentages – padding: 1%;

Strings

  • Quoted and Unquoted Strings – font-family: “Helvetica”, Arial, sans-serif;

Colors

  • Hex – color: #ccc;
  • RGB – background-color: rgb(255, 255, 255);
  • RGBA – border-color: rgba(0, 0, 0, .5);
  • HSL (Hue Saturation Lightness) – border-color: hsl (155. 33%, 65%);
  • Named Color – border-color: green;
    • Check SASS documentation for full list of named color spaces

Maps

Lists represented by key-value pairs

$colors: (
1: #ccc,
2:#333,
'color-3':red
);

The above example shows a map with key value pairs.  Both the keys and values can be either numbers or strings, and final pair is not followed by comma (think JS).  Note the parenthesis instead of the curly brace to make a map.

$margins: (

tight: 10px 20px,

compact: 30px.

large: 5em

);

A comma separates the key-value pairs, so if your declarations have commas then you need to wrap each value in parenthesis

$fonts: (
option-1: (Arial, sans-serif),
option-2: (Times, serif)
);

To then use the maps, use a function called map.get:

footer {
background-color: map-get($colors, 2);
margin: map-get($margins, large);
};

Operations

SASS supports operations with:

  • Numbers
  • Colors
  • Strings
  • Booleans
  • Lists

Numbers

Can use standard arithmetic operations:

  • Add
  • Subtract
  • Multiple
  • Divide
  • Equalities

{margin: 10px + 20px} becomes {margin: 30px} when output.  This only works if both values are of the same measurement (unit) type, so cant add pixels and em’s or %’s.  Subtraction works in a similar way.  Exception multiplication, can’t have units on both numbers {margin: 5px x 2} outputs to 10px, couldn’t have {margin: 5px X 2px}.

Division is also different.  You cant simply divide two numbers like {margin: 20px / 2;}, you have to wrap the operation in parenthesis, like {margin: (20px / 2);} outputs to {margin: 10px}.  Its also common to use variables with division, so if $baseline: 2em; and {margin: $baseline  / 2;} outputs to {margin: 1em;}.

You can do multiple operations within a single style declaration, such as {padding: 10px + 20px / 2;} outputs to {padding: 10px;}.  It follows the common order of operations (MDAS).  Just like in math, parenthesis can alter the normal order, and you evaluate what is in the parenthesis first.

Can also use inequalities like greater or less than to output to booleans, which can then be used to string together larger operations and style scripts, so {content: 10px > 20px;} outputs to {content: false;}.

Strings

Most common string operation is concatenation.  {font-family: sans + "-serif";} outputs to {font-family: sans-serif;}.  Conversely, if you have the quoted content as the first value, such as {font-family: "sans" + -serif;} the entire value is qutoed, {font-family: "sans-serif";}.  It all depends on which data comes first in the operation.  This is an important distinction.

Can also use interpolation with strings, such as $content: “default”; and {content: "This is a #{$content} button with a width of #{5 * 23} px";} outputs to {content: “This is a default button with a width of 115 px”;}.  Note that interpolating the string content removes the quotes.  Also note that we can run operations with the # sign and {}, but otherwise it would simply display as string data.

@import Directives

@import is used to import sass and scss files.  In basic CSS, @import allows us to import styles from other sheets.  SASS extends to allow us to import both SCSS and SASS files.  Imported files will be merged together in CSS output file.

It is common to separate general layout styles from page specific styles, and we can use @import directives to accomplish this.  If all the SCSS files are in the same folder, just @import "example.scss";  will import the external styles into the current document.  So, @import "generalLayout.scss"; and @import "pageSpecific.scss"; .  Compiler will import the files into the css document and output one CSS file with all styles on the document (not the @import commands, the styles themselves).

If the files are in another directory, must tell SASS how to find the document, such as @import "folderLevel/example.scss"; .  Can also skip the filename extension and SASS assumes you are looking for .sass or .scss files.

You can import multiple files in a single import directive:

@import "folderLevel/example", "otherExample";

When doing this, the order in which you import the styles is the order in which they are brought onto the page, so the output CSS document will have the example.scss before otherExamples.scss styles.  Because best practices are to write more general styles at the top of the document and more specific styles further down, be careful to import your styles in the correct order.

You are able to compile a SASS @import to a CSS @import if the file you are importing is a CSS document, not a SCSS:

@import "exampleStyle.css"; will output to @import url(exampleStyle.css); and not place the actual styles within the referring stylesheet.  It also happens when you import a url, so if you @import url("http://fonts.googleapis.com/css?family=Oswald"); you get @import url("http://fonts.googleapis.com/css?family=Oswald"); .  Finally, if you have the full hypertext link to the file, @import "https://example.com/style.css"; you will initiate a css import of @import url("https://example.com/style.css");

Media Queries

You can also place the media check within the scss import, such as:

@import "layout" print; will call the layout.scss file for import with the print media, or @import "layout" print;

Partial Files

Partial files are SCSS or SASS files that can be imported but are not compiled.  Partial files start with an underscore.

You cannot have a SCSS file partial (with underscore) and a CSS file of the same name in the same folder.

Partials are used to organize your css files without worrying about the SASS compiler transforming your files into CSS.

Be careful of the order your place your partials.  The same specificity and order effects are used for partials as for all other css files.

@media Directives

The big addition to @media drectives in SASS is the ability to write the media directive inside the element you would like to be altered depending on the media state, for example:

If you have a navigation element that you want floated right on tablets, you can write:

nav {
@media screen and (max-width: 768px;) {
float:right;
}

a {
display:block;
}
}

Usually, we write our media queries at the top or bottom of the page, but SASS allows us to write the query within the selector itself.  This will output in proper CSS syntax (at the root level of the document), but we can write it more effectively in SCSS then compile it.

@media queries can also utilize variables (using interpolation):

@media #{$tablet}

with

$tablet: "screen and (max-width: 768px)";

can be compiled properly.

Can also nest @media queries inside other @media queries.  They are combined using the and operator.

@extend Directive

This allows a selector to inherit styles from another selector.  If you have a root class styling (for example, button styling with font-size and padding) but want to have multiple button types, some larger and some smaller, you can now:

.btn {

font-size:12px;

padding: 2em;

}

.btn-large {

@extend .btn;

font-size: 18px;

}

.btn-large will now pull in all styles for the .btn class, but extend the class with an over right of the font-size.

Chain Extends

Can also perform chaining when using your @extend:

.btn-xlarge {

@extend .btn-large;

font-size: 24px;

}

Multiple Extends

Can utilize extends multiple times.  Just place both @extend directives with the class they are inheriting and your css will inherit the styles in that order.

Working with @media Directives

Can also place @extend directives inside @media directives, but the root classes you inherit must exist within the same @media directive.

@if and @for Directives

@if is used to write conditional statements while @for is used to repeatedly outputting certain styles.

The @if directive takes an expression, evaluates the expression, and if any value returns (other than false or null) then the styles inside that expression are used.  For example:

$type: serif;

body {

@if @type == serif {

font-family: serif;

}

}

With a variable set to serif, we can test that variable with the equality operator to check if the values are equal.  If they are, then SASS uses the style inside the expression (font-family, in this instance).

@if also works with @else to provide a default and @else if to test another expression.  You can use as many @else if, for use like switch case statements, for example:

$theme: neutral;

.button {

@if $theme == warm {

background-color:orange;

} @else if $theme == cold {

background-color:blue;

} @else {

background-color:gray;

}

}

If the first two conditions aren’t met, then the scripting engine moves to the @else statement

@for Directives

@for $i from 1 to 3 {

.cell {

background:blue;

}

}

This is a bit more complicated.  You say the directive, then an iterating variable ($i) and give it a margin in which to run.  The above states 1 to 3, so the expression loops 2 times (if you have the keyword to in your looping, the script doesn’t include the larger number).  If you want to include the upper limit number, use the keyword through instead of to.

You can also use variables with your @for loops, which is probably the only meaningful way to deploy these:

$comments: 10;

@for $i from 1 through $comments {

#comment-#{i}:before {

content: "#{$i}";

}

}

We are using the variable to determine how many times we want to loop.  Then we are using interpolation to have the scripting engine output the #comment- with the iteration number appended to the end of it, before the :before psuedo-class (#comment-1, #comment-2).  Inside the style, we are adding content to the style, reading out the current iteration number.

We can also iterate in reverse order.  So, rather than moving from 1 to $comments, you can move from $comments to 1, but the logic will be the same.

@each and @while Directives

Similar to the @for directive in that they repeatedly output certain styles, these directives have very specific uses.

The @each directive will run through a map or a list and can deal with multiple variables:

@each $section in featured, about, contact {

##{$section}-section {

background-image: url('img/bg-#{section}.jpg');

}

}

SASS uses the variable mixed with the list of elements to set the value for “$section” value at each element in the list.  In first iteration, #section will have the value of “featured”, in the 2nd it will have the value of “about”, and in the third, “contact”.  The above code creates three declarations, using interpolation to set the section title differently for each iteration.  SASS can also run through multiple variables with more complex lists:

@each $section, $color, $word in (featured, red, hope), (about, blue, abundance) {

.section-#{section} {

background-color: $color;

content: "$word";

}

}

SASS will iterate twice, creating styles with:

.section-featured {

background-color: red;

content: "hope";

}

and

.section-about{

background-color: blue;

content: "abundance";

}

You can also break variables out into a map as opposed to a list, and everything works the same:

$section-colors: (

featured: red,

about: blue

);

@each $section, $color in $section-colors {

.section-#{$section} {

background-color: $color;

}

}

This outputs the same two lines, but is more readable because the variable and value data are abstracted.  You can also more easily add items to the map.

@while Directive

Repeatedly outputs the nested styles until a certain statement evaluates to false, for example:

$i: 1;

@while $i < 10 {

.item-#{$i} {

top: 5em * $i;

$i: $i + 1;

}

}

First off, $i is set to one.  Then we run a loop until $i reaches 10, thus ending the loop.  Inside this loop, we set a class name with the $i variable, then we set a top offset with the value being a multiple of $i.  We then must increment the variable so the loop may run again with the new data.  Stops once the inequality evaluated to false.  Can increment in larger steps, if desired.

@mixins Directive

@mixins allow us to create reusable styles, for example:

@mixin featured {

font-size: 120%;

font-weight: bold;

border-color: #ff0000;

}

First we use the @mixin namespace and give it a name (featured, in this case).  Then, if we ever want to add these styles to any element, we just have to use an @include directive with the name we have given the mixin (@include featured).

Of course, this ability is the same as the @extends directive, so why are @mixins such a big deal?

@mixins allow us to perform more complex operations, like accepting arguements:

@mixin round($radius) {

border-radius: $radius;

}

.button {

@include round(5px);

}

This outputs to .button {border-radius: 5px;}, allowing us to set variable names above but pause variable values until runtime.

We can set default values for the variables by adding that data to the @mixin:

@mixin round($radius: 10px) {

border-radius: $radius;

}

This is the same code as above, but not the radius has a default value, so if no value is passed into the @mixin, SASS can still set the value.

@mixins can also include selectors, for example:

@mixin border-box {

* {

box-sizing: border-box;

}

}

@include border-box;

Outputs to * {box-sizing: border-box;} which sets all elements to have border-box sizing.

Variable Arguments

@mixin box-shadow($args...) {

-webkit-box-shadow: $args;

box-shadow: $args;

}

.element {

@include box-shadow(1px 1px 5px 0 #000000);

}

Because we have some styles with multiple values in a single declaration (box-shadow has x, y, size, and color), we use the $args… value for our box-shadow namespace, then we pass in the arguments (shadow details) and SASS interprets them correctly.  We can even have multiple sets of arguments:

.element-2 {

@include box-shadow(1px 1px 5px 0 #000, 2px 10px 15px rgba(0, 0, 0, .5));

}

This outputs correctly and places multiple shadows on .element-2.

Nested @Mixins

@mixins can also include other @mixins:

@mixin round($radius) {

border-radius: $radius;

@include featured;

}

Then, when a style is set that invokes the round @mixin, the code from the featured @mixin is added as well.