Let's get Sassy 💅
Tags: webdev, css, codenewbie, design
Published: 2020-12-07 07:34:14

# What is Sass? 

Sass or _syntactically awesome stylesheets_ is a preprocessor for CSS that fixes many of the pain points most developers have with CSS. 

There are two types of Sass 
1. SCSS (`.scss`) → "sassy css" 
2. Sass (`.sass`) → "syntactically awesome stylesheets" 

Here we are going to focus on `.scss` version of Sass since most the syntax for `.scss` is _nearly_ the same as traditional `.css` files, and will be more useful to most developers than learning an entirely new syntax. Along the same vein, comparing `.scss` with `.css` files will be easier than `.sass` files since they look more python-like in their syntax. 

# What's wrong with CSS? 😑

Since Sass is a preprocessor of CSS, you could write `.scss` files in vanilla CSS, but you'd be missing out on all the awesome features that the Sass preprocessor provides. The core of those features really aim to address the scalability and maintainability of CSS in modern web applications. 

With sassy css, you have all the power of CSS... plus
* variables (_technically you can use variables in `.css` files, but its just better with sass)
* nesting 
* mixins 
* inheritance 

# Getting Started  🏁

So before you can do anything, you've need to get the Sass compiler installed. For the latest instruction, check out the [sass install documentation](https://sass-lang.com/install), but for the `tl;dr;` version you can get the Sass compiler setup globally using Node.js/NPM using the `sass` module. 
```sh
npm install -g sass
```
You can also install the Sass preprocessor using homebrew. 
```sh
brew install sass/sass/sass
``` 
> this installs the Node sass implementation of the pre-processor which is [currently deprecated](https://github.com/sass/node-sass#node-sass) - while the sassy engineering team has said that this package will continue to receive updates the most up-to-date version is [dart sass](https://sass-lang.com/dart-sass)

Once its installed, you can compile `.scss` files (or `.sass` files) using `sass <source-file> <output-file>` like so 
```sh 
sass source/stylesheets/index.scss build/stylesheets/index.css
```

With the compiler installed, lets get sassy! 

# Sass Selectors 💃

As mentioned previously, files written in `.css` are 100% valid as `.scss` files. So comments and selecting html elements can be done in the same syntax: 
* comments 
    * single line comments can be made using a double slash (`//`) 
    * multi-line comments can be denoted slash-start (`/*`) and  star-slash (`*/`) , with the `/*` denoting the start of the multi-line comment and the `*/` denoting the end of the multi-line comment 
* selectors 
    * tags 
        * tags are selected simply by placing the tag name 
        * `header` selects `<header>` element 
    * classes 
        * select classes using `.` followed by the class name (e.g. `nav-links`)
        * `.nav-link` selects any elements with a `class="nav-link"`
    * id 
        * select an element by it's id using `#` followed by the id 
        * `#root` selects any element with `id="root"`  
* block 
    * the actual rules of `.css` and `.scss` files 
    * defined between curly braces (`{}`)
    * each line / statement should terminate with a semi-colon `;` 

```scss 
/* select an element by using its name */ 
body {
    display: flex; 
}
/* select an element by its ID */
#root {
    margin: 0; 
}
/* selecting elements by class */
.nav-links {
    text-decoration: none; 
}
```

# Sassy selecting 👌 

One of the first features `.scss` improves on from `.css` is the selection of **nested elements**. Imagine you are styling a basic navbar for a website: 

```html
<!--- index.html -->
<html>
  <body> 
    <header>
      <nav>
        <span class="logo">Sassy</span>
        <ul>
          <li>
            <a href="#">Home</a> 
          </li>
           <li>
            <a href="#">About</a> 
          </li>
           <li>
            <a href="#">Contact</a> 
          </li>
        </ul>
      </nav> 
    </header>
  </body> 
</html> 
```

[codepen](https://codepen.io/collection/XkmkPv) 

You might do something like: 

```css 
/* CSS */
html {
  margin: 0; 
  padding: 0; 
}
header {
  border-bottom: 1px solid #1F7A8C; 
}
nav {
  display: flex; 
  margin: 0 2rem; 
  justify-content: space-between; 
}
nav span.logo {
  font-family: cursive; 
  font-weight: bold; 
  font-size: 2rem; 
  align-self: center; 
  height: 50px; 
  width: 100px; 
  border-radius: 50px; 
}
nav ul {
  display: flex; 
  align-items: center; 
}
nav ul li {
  list-style: none; 
  display: flex; 
  height: 50px; 
  width: 100px; 
  border-radius: 50px; 
  background: #BFDBF7;
  justify-content: center;
  margin: 0.2rem 1rem;
  border: 1px solid #1F7A8C; 
  color: #053C5E; 
}
nav ul li a {
  color: inherit; 
  font-family: monospace;
  align-self: center; 
  font-weight: bold; 
  font-size: 1.3rem;
  place-content: center; 
  text-decoration: none; 
 }
``` 

# Nesting selectors ⇣

Instead of repeating `nav ul` at the beginning of each selection statement as we did in or `.css` file, we can nest the selector definitions inside one another. 

```scss 
html {
  margin: 0; 
  padding: 0; 
  header {
    border-bottom: 1px solid #1F7A8C; 
    nav {
        display: flex; 
        margin: 0 2rem; 
        justify-content: space-between; 
        span.logo {
            font-family: cursive; 
            font-weight: bold; 
            font-size: 2rem; 
            align-self: center; 
            height: 50px; 
            width: 100px; 
            border-radius: 50px; 
          }
          ul {
            display: flex; 
            align-items: center; 
          }
          li {
            list-style: none; 
            display: flex; 
            height: 50px; 
            width: 100px; 
            border-radius: 50px; 
            color: #053C5E; 
            background: #BFDBF7;
            justify-content: center;
            margin: 0.2rem 1rem;
            border: 1px solid #1F7A8C; 
            a {
                color: inherit; 
                font-family: monospace;
                align-self: center; 
                font-weight: bold; 
                font-size: 1.3rem;
                place-content: center; 
                text-decoration: none; 
               }
          }
      }
  }
}
```

Notice how each selector is **nested** inside the curly-braces (`{}`) comprising it's parent selector. 

Notice how the `.scss` version shorter, but it's easier to understand and easier to read. 

Solely with the information of the `.scss` file above, we know that the `<nav>` element is a child of the `<header>` element because the `nav` selector is nested inside the `header` selector. We don't need to repeat selectors in nested selector statements either. 

> In this simple example, the benefit might seem negligible, but this becomes increasingly valuable as your stylesheets grow in size. 

# Sassy Pseudo selectors 🥸

Given the previous `.html` file we looked at before, how would you add the `:hover` pseudo selector in CSS? 

```css
/* redefine the entire selector hierarchy (nav ul li) and apped pseudo selector */
nav ul li:hover {
    transition: 1s all ease; 
    color: #000;  /* make the text black */
    filter: invert(100%);  /* invert the default colors */
}
```
The Sassy way emphasizes the DRY principle, instead providing its own pseudo selector to address these style definitions. 

In Sass, the ampersand (`&`) represents the parent selector, and enables chaining. So if we can define seperate styles for mouse over (`:hover`), visited pages (`:visited`), the last item (`nth()`) and all the other pseudo selectors like so 

```scss
/* abbreviated .scss file */
/* css removed from most of document for simplicity */ 
html { /* styles.. */
  header { /* styles.. */
    nav {  /* styles.. */
        span.logo { /* styles.. */}
          ul { /* styles.. */}
          li {
            /* & represents parent (li) */ 
            /* ⬇️ hover styling ⬇️ */
            &:hover {
                transition: 1s all ease; 
                color: #000;  /* make the text black */
                filter: invert(100%);  /* invert the default colors */
            }
            a { }
          }
      }
  }
}
```

The `&` refers to the selector parent, so since we are in the body of the `li`'s styling, the `li` tag is the parent select the `&` symbol is referencing. 

We can use the `&` operator in our sassy `.scss` files in conjunctions with all the same [pseudo selectors](https://www.w3schools.com/css/css_pseudo_classes.asp) as `.css` files. 

```scss 
li {
    &:hover {
        /* hover pseudo selector */
    }
    &:focus {
        /* focus pseudo selector */ 
    }
}
a {
    &:visited { 
        /* visited pseudo selector */ 
    }
}
```

But we can get sassier. In `.scss` we can truly embrace the DRY principles and use the `&` operator to append to the parent selector. 

So if we have an element with the class `.hero`, and inside that element, there are other elements that have classes starting with `.hero`, we can append to the class selector using the `&` operator:

```scss 
.hero {
    &-container {
        /* equivalent to `hero-container` */
    }
    &-title {
        /* equivalent to `hero-title` */
    }
    &-sub-title {
        /* equivalent to `hero-sub-title` */
    }
}
```
## 🕺 Variables with sass 


Variables in `.scss` files start with the dollar-sign symbol (`$`), and can store any valid CSS value. 

```scss
$primary-color: #053C5E; 
$secondary-color: #BFDBF7; 
```

We can then assign the values stored in our sassy variables simply by placing where ever we need it: 

```scss
body {
    background: $secondary-color; 
    color: #053C5E; 
}
```

When the `.scss` file is run through the Sass compiler, the variables values will be replaced with the value assigned to the variable. While native CSS does allow for variables, Sass does it better. 

Another benefit to `.scss` variables is that they can be mutated from the start of the file to the end, without altering the v

Sass variables support being mutated from the start of the file to the end. If you assign `$primary-color: #053C5E`, on line 10, and re-assign it on line 254 mutated the value such that `$primary-color: #000`, every subsequent block will substitute `$primary-color` will receive the color code of `#000`, while every element before its re-assignment on line 254 will remain the original value of `#053C5E`. 

| Sass                                                                     | CSS                                                                                             | Ramifications                                                                                               |
| ------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| Sass variables are replaced with their values by the Sass compiler       | CSS variables are loaded and interpreted by the browser                                         | more overhead for the client browser                                                                        |
| Sass variables are pre-fixed with `$varName` and called using `$varName` | CSS variables are declared using `--varName` and are scoped to the tag that they are defined in | using variables is more complex, syntactically longer, and more difficult to read                           |
| Sass is transpile to basic CSS                                           | CSS variables are only supported by newer browsers (explorer will not work)                     | Sass compiles `.scss` files such that the resulting configuration has more compatibility than CSS variables |

Just like most developers are used to, variables defined using `$varName` are scoped to the block, (i.e. between `{` and `}`) that they were defined in. If you define at the root of your `.scss` document, it will be scoped to the entire document, define it within the scope of a selector it will be scoped to the parent selector and its children. 

```scss 
body {
    /* variables defined here will only be accessible within the body 
     * of the selector and its nested selectors */
    $font-size: 12px; 
    p {
        /* 👍 we CAN access $font-size here ✅ */
    }
}
/* 👎 we CANNOT access $front-size here ⛔️ */
```

Sass variables mutated from the original values start of the file to the end. If you assign `$primary-color: #053C5E`, on line 10, and re-assign it on line 254 mutated the value such that `$primary-color: #000`, every subsequent block will substitute `$primary-color` will receive the color code of `#000`, while every element before its re-assignment on line 254 will remain the original value of `#053C5E`. 

```scss 
$color: #333; 
body {
    $color: #444; /* mutation scoped to this block */
    p: $color: ; /* color → #444 */
}
footer {
    p: $color; /* color → #333 */
}
```

# Sassy Mixins and At-rules 🌀

Mixins are like JavaScript functions: 
* they can be declared as a variable and re-used in multiple locations 
* they can take 0 or more arguments 
* they can be scoped (like a function assigned to `const`) 

You begin a _mixin_ with `@mixin` followed by the name of the _mixin_ you are defining. 

Then place all the `.css` rules between the opening curly brace (`{`) and the closing curly brace (`}`) that make up the mixins body.  

```scss
/* reset properties associated with a list */
@mixin reset-list {
  margin: 0;
  padding: 0;
  list-style: none;
}
``` 

We can then use apply our `@mixin reset-list`  anywhere it makes sense to do so, **including inside another `@mixin` definition** using the `@include` at-rule, followed by the name of the mixin (e.g. `reset-list`). 

```scss
/* apply mixin to navbar  */
nav ul {
  @include horizontal-list;
}

/* apply mixin to another mixin */
@mixin horizontal-list {
  @include reset-list;

  li {
    display: inline-block;
    margin: {
      left: -2px;
      right: 2em;
    }
  }
}


```
> [sass docs](https://sass-lang.com/documentation/at-rules/mixin) 

Adding arguments to sassy `@mixin` is remarkable similar to the syntax used in JavaScript as well. 

The `@mixin variable-name` remains the same, but before the opening curly brace defining the `.css` properties, we place any parameters delimited by commas (`,`) and surrounded by parenthesis. 

```scss 
@mixin rtl($property, $ltr-value, $rtl-value) {
  #{$property}: $ltr-value;

  [dir=rtl] & {
    #{$property}: $rtl-value;
  }
}

.sidebar {
  @include rtl(float, left, right);
}
```
Every rule previously mention applies here as well. Each variable (`$variableName`) will be subsituted for the parameter provided to the mixin when we declare the mixin using the `@include mixin-name`  followed by the parameters wrapped in parenthesis. 
* mutating a mixin will only affect subsequent code 
* same scoping rules apply 
* same syntax for declaring and using variables 

We can define default values for `@mixins` using a colon (`:`)

```scss 
@mixin replace-text($image, $x: 50%, $y: 50%) {
  text-indent: -99999em;
  overflow: hidden;
  text-align: left;

  background: {
    image: $image;
    repeat: no-repeat;
    position: $x $y;
  }
}

.mail-icon {
  @include replace-text(url("/images/mail.svg"), 0);
}
```
## Interpolation 

Interpolation can be used almost anywhere in a Sass stylesheet. 
* to define classes 
* to define css properties 
* to define values of css properties 
* to combine variables 



```scss
@mixin inline-animation($duration) {
  $name: inline-#{unique-id()};

  @keyframes #{$name} {
    @content;
  }

  animation-name: $name;
  animation-duration: $duration;
  animation-iteration-count: infinite;
}

.pulse {
  @include inline-animation(2s) {
    from { background-color: yellow }
    to { background-color: red }
  }
}
```

* Using a variable in a property value doesn't require interpolation. (e.g. `color:#{$accent}` === `color: $accent`) 
* interpolated variables will be returned as strings. 


---

# Mixin and Flow Control 🪖

Mixins provide for a variety of flow control  with `@if`, `@else`, `@while` ,`@each` and `@for` at-rules. 
* `@if` determines whether or not a block is evaluated.
* `@each` evaluates a block for each element in a list or each pair in a map.
* `@for `evaluates a block a certain number of times.
* `@while` evaluates a block until a certain condition is met.
 
```scss 
/* only apply a border-radius if the the radius is not 0  */
@mixin square($size, $radius: 0) {
  width: $size;
  height: $size;

  @if $radius != 0 { /* must evaluate to True/False */
    border-radius: $radius;
  } 
  @else { /* can only be included if there is an `@if` at-rule */
    border-radius: unset; 
  }
}
```
You can implement `if` → `else if` → `else` logic by inserting a `@else if` at-rule following an `@if` at-rule and before an `@else` at rule

```scss
@mixin triangle($size, $color, $direction) {
  height: 0;
  width: 0;

  border-color: transparent;
  border-style: solid;
  border-width: $size / 2;

  @if $direction == up {
    border-bottom-color: $color;
  } @else if $direction == right {
    border-left-color: $color;
  } @else if $direction == down {
    border-top-color: $color;
  } @else if $direction == left {
    border-right-color: $color;
  } @else {
    @error "Unknown direction #{$direction}.";
  }
}
```
> anything with a determined value is truthy and anything that is `null` is falsey 
 
## Looping ➰

We can implement looping in `@mixin`s using `@for` at-rule and interpolation. 

```scss 
@mixin order($height, $selectors...) {
    /* increase the margin top for each element selected 
     * in $selectors */
  @for $i from 0 to length($selectors) {
      /* interpolate the selector using the `nth` function */
    #{nth($selectors, $i + 1)} {
      position: absolute;
      height: $height;
      margin-top: $i * $height;
    }
  }
}
``` 

The `@each` at-rule works similarly: 

```scss
/* collection of sizes */
$sizes: 40px, 50px, 80px;

/* loop through the values applying the size to the 
 * matching class (e.g. `icon-50px` ) */
@each $size in $sizes {
  .icon-#{$size} {
    font-size: $size;
    height: $size;
    width: $size;
  }
}
```

You can also loop through collections using the `@while` mixin, but doing so typically is implented using functions, which definitely goes beyond the basics, and we've already covered a lot here. 

# Wrap up 

These are the fundamentals of Sass, but there's lots more to it. There's more [at-rules](https://sass-lang.com/documentation/at-rules) as well as functions and modules. However, even without functions, or mixins, Sass makes styling a more readable, more maintainable, and more concise way to add styling to html. 

So the question is, do you want to get sassy? 💅