Skip to content

Instantly share code, notes, and snippets.

@pixelspencil
Last active October 31, 2023 18:29
Show Gist options
  • Save pixelspencil/12e596efdfe94b758a858457b815015e to your computer and use it in GitHub Desktop.
Save pixelspencil/12e596efdfe94b758a858457b815015e to your computer and use it in GitHub Desktop.
SASS 101

Sass 101

Preprocessing

Once you start tinkering with Sass, it will take your preprocessed Sass file and save it as a normal CSS file that you can use in your website.

The most direct way to make this happen is in your terminal. Once Sass is installed, you can compile your Sass to CSS using the sass command. You'll need to tell Sass which file to build from, and where to output CSS to. For example, running sass input.scss output.css from your terminal would take a single Sass file, input.scss, and compile that file to output.css.

sass --watch input.scss output.css

You can also watch individual files or directories with the --watch flag. The watch flag tells Sass to watch your source files for changes, and re-compile CSS each time you save your Sass. If you wanted to watch (instead of manually build) your input.scss file, you'd just add the watch flag to your command, like so:

sass --watch app/sass:public/stylesheets

Working with basic variables

If there's one reason to learn to use Sass, it's variables. It made working with CSS a lot easier.

Basic variable syntax & use

$offwhite: #eee8d6;
$darkblue: #022933;
$yellow: #ffba00;
$blue: #0076a3;
$green: #548c27;
$orange: #f39856;
$red: #d14348;
$purple: #6d73c2;

Example of use

body {
  color: $darkblue
  background-color: $offwhite;
}

Variables referencing variables & use

$color-main: $darkblue;
$color-backgrounds: $offwhite;
$color-headlines: $red;

Example of use

body {
  color: $color-main
  background-color: $color-backgrounds;
}

Fonts stored in variables & use

@import url(http://fonts.googleapis.com/css?family=Roboto+Slab|Merriweather|Josefin+Slab|Oswald|Bree+Serif);

$color-main: $darkblue;
$color-backgrounds: $offwhite;
$color-headlines: $red;

Example of use

body {
  font-family: $font-main;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-family: $font-highlight;
  color: $color-headlines;
}

Nesting your styles

Another really popular Sass feature is called nesting. It allows you to put CSS rules inside other rules and it's a great way to organize your styles so that they're super easy to find.

NOTE: Only go to a max of 4 levels deep when nesting styles

HTML layout to style

<h1>Media</h1>
<ol class="media">
  <li class="item">
    <a href="#">
      <img src="http://placehold.it/90x90" alt="thumbnail" />
      <h2 class="head">Title one</h2>
      <p>
        Atque suscipit, neque quisquam laboriosam enim officiis, nam nemo
        cupiditate ipsam eveniet id eaque optio facere! Maxime accusamus
        repellendus nisi veniam saepe explicabo, voluptatum quod obcaecati,
        possimus quis expedita? Dignissimos.
      </p>
    </a>
  </li>
</ol>
... ...

Nested SASS reflecting html layout

$color-item-border: $red;
$color-media-head: $blue;

.media {
  margin: 0;
  padding: 0;
  list-style: none;

  .item {
    margin-bottom: 20px;
    border-top: 1px dotted $color-item-border;
    padding-top: 10px;
  }

  .head {
    margin: 0;
    padding: 0;
    color: $color-media-head;
    font-size: 2rem;
  }

  p {
    margin: 0;
    font-size: 1.5rem;
  }

  img {
    float: left;
    margin-right: 10px;
  }

  a {
    text-decoration: none;
    color: $color-main;
  }
}

Rendered CSS

.media {
  margin: 0;
  padding: 0;
  list-style: none;
}
.media .item {
  margin-bottom: 20px;
  border-top: 1px dotted #d14348;
  padding-top: 10px;
}
.media .head {
  margin: 0;
  padding: 0;
  color: #0076a3;
  font-size: 2rem;
}
.media p {
  margin: 0;
  font-size: 1.5rem;
}
.media img {
  float: left;
  margin-right: 10px;
}
.media a {
  text-decoration: none;
  color: #022933;
}

Using Partials

Sass allows you to break up your CSS into modules called partials. That also helps you organize your code into logical groups. So it's quite easy to do. You simply add an @import command just like you would do in CSS. However, what is different is that the CSS gets actually combined into a single file when it is output.

Using the @import command

When you have made your separate .scss files you use the @import command to reference each file in the order to be imported and processed in to one file, any files in sub folders should also be referenced correctly relative to the main style.scss file.

NOTES:

  1. When using @import make sure each reference does not have .scss file extension - it is not required - if it is left it each file will be processed into a separate .css file.
  2. Make sure each .scss file that will be imported as a partial has a filename beginning with an underscore e.g. _somesassfile.scss.
  3. If you do want to make sure that something gets imported in the traditional way where it's another server call, then you can import a document that has a .css extension. It will treat it just like a normal import or give Sass a URL that begins with http:// or give it a file name that is a URL like we have done here in variables.scss.

Example filenames for partials

  • _base.scss
  • _normalize.scss
  • _variables.scss
  • style.scss
@import "variables";
@import "normalize";
@import "base";
@import "modules/media";

Using basic mixins

Another powerful feature in SASS is the ability to create JavaScript-like functions called mixins. If you're familiar with JavaScript, these are going to make a lot of sense.

Structure of a mixin

/* background image mixin */
/* backImage ($var, $var, $var: fallback, $var: fallback) {} */
@mixin backImage($image, $height: 100vh, $bgPos: center center) {
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.6)),
    url($image);
  background-repeat: no-repeat;
  background-position: $bgPos;
  background-size: cover;
  height: $height;
}

/* clearfix mixin */
@mixin clearfix {
  &:before,
  &:after {
    content: "";
    display: table;
  }
  &:after {
    clear: both;
  }
}

Using the mixin

/* @include backImage ( 'imgurl', 'var', [falls back] ) {} */
.jumbotron {
  @include backImage("../images/water.jpg", 600px);
}

/* @include backImage ('imgurl', 'var', var) {} */
.bridge {
  @include backImage("../images/bridge.jpg", 300px, center center);
}

/* using the clearfix mixin on &:last-of-type*/
.item {
  padding-bottom: 10px;
  padding-top: 10px;
  padding-left: 10px;
  border-top: 1px dotted $color-item-border;
  &:hover {
    background: $yellow;
  }
  &:last-of-type {
    border-bottom: 1px dotted $color-item-border;
  }
  @include clearfix;
}

Using the @extend method

This uses one style as the basis for a second style. To define the code to act as a basis for other @extend styles you use the % (invisible class) before the name of the style:

/* variables */
$color-btn-text: $color-backgrounds;
$color-btn-default: $color-main;
$color-btn-hot: $red;
$color-btn-cool: $blue;
/* extends style */
%btn {
  display: inline-block;
  padding: 6px 12px;
  text-align: center;
  white-space: nowrap;
  vertical-align: middle;
  cursor: pointer;
  border: none;
  border-radius: 4px;
  font-family: $font-highlight;
  user-select: none;
  color: $color-btn-text;
}

Then when you want to extend using the above you'd used @extend like in the examples below:

.btn-default {
  @extend %btn;
  background: $color-btn-default;
}

.btn-hot {
  @extend %btn;
  background: $color-btn-hot;
}

.btn-cool {
  @extend %btn;
  background: $color-btn-cool;
}

Referencing parent selectors with &

Sass uses the ampersand character as a special selector that can be used to reference the parent of an element. And you can use this type of selector to solve a variety of problems.

Using the & allows you to select the parent of the current style code block you are in an add styles to that e.g. when you hover on the parent and want a background colour to show.

$color-item-border: $red;
$color-media-head:  $blue;

.media {
  margin: 0;
  padding: 0;
  list-style: none;

  .item {
    padding-bottom: 10px;
    padding-top: 10px;
    padding-left: 10px;
    border-top: 1px dotted $color-item-border;

    /* the below style will add a yellow hover when parent is hovered on */
    &:hover {
      background: $yellow;
    }

    /* the below style will add a border to the bottom of the last item inside the parent */
    &:last-of-type {
      border-bottom: 1px dotted $color-item-border;
    }
    @include clearfix;
  }

Using & to create contextual styles based based on content structure

The below is saying "if there is an element with #typography and inside a nested element with .head then apply this style:

  .head {
    margin: 0;
    padding: 0;
    color: $color-media-head;
    font-size: 2rem;
    #typography & {
      color: $red;
    }

Using comments and hidden comments

/* ... */ normal comment for SCSS

/* this is a normal comment */

/_! ... _/ force comment, shows in compressed mode

/*! this is a force comment */

Invisible (JS style) comment

// this is an invisible comment

Working with math operators

SASS has operations like addition, multiplication, division, and others, these can help to create more complex CSS styles.

NOTE:

  • Make sure when using operations that there is correct spacing, if not then these may be compiled as names rather than operations.
/* @mixing imageGrid($var, $var) {} */
@mixin imagegrid($qty, $margin) {
  // set the width to 100%
  // set amount of items
  // deduct one item
  // mutliply that with the margin value that is specified when user passes var in
  // take that whole thing and divid by amount of items from user

  // Defines proper width of the element with margins only inside the element items.
  width: ((100% - (($qty - 1) * $margin)) / $qty);

  // then use & to call parent and ask for nth-child operator
  // This says on every element, go ahead and give it the margin that we're specifying when we pass this variable called margin along
  &:nth-child(n) {
    // set margins on bottom and right to what user sets
    margin-right: $margin;
    margin-bottom: $margin;
  }

  // then use #{$var} to insert a variable
  // this targets the last item
  // Then on the last one just set the margin of that last element to zero.
  &:nth-child(#{$qty}n) {
    margin-right: 0;
    margin-bottom: 0;
  }
}

Using the above mixin

.grid {
  @include clearfix;
  .item {
    float: left;
    // @include imageGrid($var, $var)
    @include imagegrid(3, 2%);
  }
  img {
    display: block;
    border-radius: 10px;
    max-width: 100%;
  }
}

Modifying colors

SASS allows you to work with colours in new ways with various functions.

// applies transparentize() to $yellow with a setting of .8
$color-even-cells: transparentize($yellow, 0.8);

.table {
  font-size: 1.5rem;
  text-align: left;
  width: 100%;

  caption {
    font-size: 2.5rem;
    text-align: left;
    padding-bottom: 5px;
  }

  > thead,
  > tbody,
  > tfoot {
    > tr {
      &:hover {
        // applies darken( adjust-hue ( ) ) to $color-backgrounds with a setting of -20deg, 10%
        background-color: darken(adjust-hue($color-backgrounds, -20deg), 10%);
      }

      &:nth-child(even) {
        background-color: $color-even-cells;

        &:hover {
          // applies opacify( ) to $color-even-cells with a setting of .3
          background-color: opacify($color-even-cells, 0.3);
        }
      }
      > th,
      > td {
        padding: 10px;
        border-bottom: 1px dotted $blue;
      }
    }
  }

  > thead > tr {
    background-color: $blue;
    color: $color-backgrounds;
    &:hover {
      // applies complement() to $blue
      background-color: complement($blue);
    }
    > th {
      border-bottom: 0;
    }
  }
}

Creating list elements

SASS has a special version of arrays called lists, these are unlike any other type of array structure.

// this is in essence a list
$roundness: 20px 0 20px 0;

// this is how it can be used
img {
  display: block;
  // this uses nth() to reference the variable but take only the first value = 20px
  border-radius: nth($roundness, 1);
  max-width: 100%;
}

Using the @content container

When calling a mixin it is possible to call some code to be processed within the mixin. A practical example of this would be media queries.

In the example below inside the break mixin, it uses the @content directive to ask this media query to be passed along some content.

This content directive allows us to pass this mixin some CSS that we want to stick inside this media query.

// @mixin break($var) {}
@mixin break($length) {
  @media (min-width: $length) {
    // @content is where CSS is passed to this mixin when referenced
    @content;
  }
}

// using the mixin
nav {
  header & {
    background-color: darken($blue, 15%);
  }

  .branding {
    float: left;
    display: none;
    // call break mixin and set width to 1000px to show the branding
    @include break(1000px) {
      display: block;
    }
  }

  .branding h1 {
    font-size: 2.75rem;
    overflow: hidden;
    margin: 0;
    color: $yellow;
  }

  ul {
    list-style: none;
    margin: 0;
    padding: 0;
    @include clearfix;
  }

  ul li {
    // call break mixin and set width to 760px to float nav right
    @include break(760px) {
      float: right;
    }
    padding: 10px 5px;
  }

  ul li a {
    text-decoration: none;
    padding: 10px 5px;
    color: $color-backgrounds;

    &:hover {
      color: $yellow;
    }

    header &:hover {
      background-color: darken($blue, 20%);
    }

    footer & {
      color: white;
    }
  }
  footer & {
    margin-top: 20px;
    min-height: 200px;
    background-color: darken($blue, 15%);
  }
}

Conditional @if statements and argument lists

Conditional statements allow you to create styles that react to changes in your CSS.

There is a special operator that can be used to create a mixin that takes up any number of values ....

Below is a modified version of the mixin from Using the @content container.

// @mixin break($var, ... (other x$var) ) {}
@mixin break($args...) {
  // if call has one param use this option
  @if length($args) == 1 {
    @media (min-width: nth($args, 1)) {
      // @content is where CSS is passed to this mixin when referenced
      @content;
    }
  }
  // if call has two params use this one
  @if length($args) == 2 {
    @media (min-width: nth($args, 1)) and (max-width: nth(args, 2)) {
      // @content is where CSS is passed to this mixin when referenced
      @content;
    }
  }
}

// If Else example
/*
@mixin breakExampleIfElse($args...) {
  // if call has one param use this option
  @if length($args) == 1 {
    @media (min-width: nth($args, 1)) {
      // @content is where CSS is passed to this mixin when referenced
      @content;
    }
  }
  // if call has two params use this one
  @else {
    @media (min-width: nth($args, 1)) and (max-width: nth(args, 2)) {
      // @content is where CSS is passed to this mixin when referenced
      @content;
    }
  }
}
*/

// using the mixin
footer & {
  margin-top: 20px;
  min-height: 200px;
  background-color: darken($blue, 15%);

  // if width is between 0 and 500px display none
  @include break(0, 500px) {
    display: none;
  }
}

Looping through lists with @for

Loops are a type of structure that can help you go through a series of items, the below example shows how to use the @for loop in SASS.

$color-headlines: $blue, $purple, $green, $red, $orange, $yellow;

@for $item from 1 through length(@color-headlines) {
  h#{$item} {
    color: nth($color-headlines, $item);
  }
}

Generates this CSS

h1 {
  color: #0076a3;
}

h2 {
  color: #6d73c2;
}

h3 {
  color: #548c27;
}

h4 {
  color: #d14348;
}

h5 {
  color: #f39856;
}

h6 {
  color: #ffba00;
}

Looping through lists with @each

Lists can become much more powerful when you can use them to go through a series of values. The below example shows how to use the @each loop in SASS.

// set up variables
$color-btn-names: "default", "hot", "cool";
$color-btn-values: $color-main, $red, $blue;

// setup @each loop function
// =========================
// for each name in $color-btn-names do the following
@each $name in $color-btn-names {
  // set index of $colour-btn-names and look for the $name, finds which item name is in that loop cycle
  $i: index($color-btn-names, $name);

  // use .btn prefix with a dash and `#`
  // this is going to be a calculated element that is going to be based on the name
  // generates a style for each item in the variable being looped
  .btn-#{$name} {
    @extend %btn;
    // gets the color value from the list based on the number generated by the loop cycle
    background-color: nth($color-btn-values, $i);
  }
}

Working with the map data type

Many programming languages have a really useful data type called associative arrays. Now, they allow you to create complex lists that have both a key for each element as well as a value. Unlike arrays, the keys aren't just numbers but can also have names. Sass has a beautiful implementation of this, so we're going to use that to make creating a series of buttons a breeze.

// set up variables
$color-btn-names: "default", "hot", "cool";
$color-btn-values: $color-main, $red, $blue;

// set up map
$color-btns: (
  default: $color-main,
  hot: $red,
  cool: $blue,
);

// setting up use of the map with @each
@each $key, $value in $color-btns {
  .btn-#{$key} {
    @extend %btn;
    background-color: $value;
  }
}

Learning objectives

  • Working with variables
  • Nesting styles
  • Creating mixins
  • Conditional statements and loops in SassScript
  • Extending your mixins
  • Working with lists and maps

Skills covered in this course

  • SASS
  • Front-end Development

Resources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment