Modern Frontend Workflow in a .NET World Part 3: Sass

Originally posted on the DCS Innovation Labs Blog.

All code for this post is available on github.

In this post we are going to take a look at the popular CSS preprocessor, Sass. As I mentioned in Part 1 of this series, CSS preprocessors allow us to do a ton of cool things, but today I'm going to focus on:

  1. Installing Sass
  2. Using Sass
  3. Project setup
  4. Using variables
  5. Using functions

Last time, we took a look at how to get up and running with NPM. I mentioned that along with being great for dependency management, NPM can also be used to install and use many frontend tools. While there are many ways that you can set up Sass on your machine, in this post I'm going to use the version available through NPM, node-sass. If you don't have NPM up and running on your machine, take a look at Part 2 of this series to get caught up.

For this post I'm going to be using PowerShell on Windows but the same steps apply on Bash, ZSH, and pretty much any other Bash-like shell.

1) Installing Sass

I know that I haven't really told you in detail what Sass does yet, but this is one of those things that's easier to understand if you can play with it while reading about it.

As we looked at in my last post, before installing NPM dependencies, we had to initialize NPM in our project with npm init. To install global command line tools, we don't have to be in a project. So to install node-sass, all we have to do is run npm install -g node-sass. The -g parameter tells NPM to install this package globally instead of in a specific project.

C:\Projects\sass_sample_app> npm install -g node-sass  
C:\Program Files\nodejs\node-sass -> C:\Program Files\nodejs\node_modules\node-sass\bin\node-sass

> [email protected] install C:\Program Files\nodejs\node_modules\node-sass
> node scripts/install.js

Binary downloaded and installed at C:\Users\JDarling\AppData\Roaming\nvm\v6.3.1\node_modules\node-sass\vendor\win32-x64-  
48\binding.node

> [email protected] postinstall C:\Program Files\nodejs\node_modules\node-sass
> node scripts/build.js

"C:\Users\JDarling\AppData\Roaming\nvm\v6.3.1\node_modules\node-sass\vendor\win32-x64-48\binding.node" exists.
 testing binary.
Binary is fine; exiting.

...

C:\Projects\sass_sample_app>  

Tools that are meant to be installed globally like this one will generally come with an executable that will be included in your PATH. As a result, you will be able to run node-sass as a command line executable. You may need to restart your shell for node-sass to be available to execute.

2) Using Sass

One of the first things we need to address is the syntax we are going to use. Sass has two different syntax options, Sass and SCSS. The Sass syntax is quite a departure from traditional CSS giving it a significant learning curve for those coming from pure CSS. SCSS is a true superset of CSS, meaning that any CSS file is also a valid SCSS file. This is a much more natural syntax to those who are familiar with CSS and is what I will be using in this post. With that said, I'll still be referring to it as Sass, as that is the commonly used term when referencing either syntax.

I'm going to make a small project using Sass here in a bit, but let's start off with getting familiar with the tool really quick.

Wherever floats your boat, go ahead and make a file called styles.scss and just write a few lines of CSS in there (nothing fancy, we'll be throwing this file away). Note that the file extension is .scss instead of .sass. This is because we will be using the SCSS syntax instead of the Sass syntax as I described above.

.container {
  background-color: blue;
}

p {  
  font-family: serif;
}
C:\Projects\sass_playground>  

As I said earlier, any CSS is also valid SCSS code, so lets run Sass on this to convert it into CSS using node-sass styles.scss styles.css. We're telling node-sass to take styles.scss, convert it to CSS, and output it in styles.css.

C:\Projects\sass_playground> node-sass styles.scss styles.css  
Rendering Complete, saving .css file...  
Wrote CSS to C:\Projects\sass_playground\styles.css  
C:\Projects\sass_playground>  

Go ahead and look at your directory and you should see two files now, styles.scss and styles.css.

C:\Projects\sass_playground> ls


    Directory: C:\Projects\sass_playground


Mode                LastWriteTime         Length Name  
----                -------------         ------ ----
-a----        8/11/2016   1:25 PM             70 styles.css
-a----        8/11/2016   1:21 PM             77 styles.scss

And if you look at styles.css, you should see the same style declarations that you put into styles.scss.

.container {
  background-color: blue; }

p {  
  font-family: serif; }

As you can see, the style rules are the same, the only change was the bracket placement. Sass leaves CSS that you write alone, and truly only makes changes when it runs into Sass specific features.

There's one more thing you should know about, and that's the --watch parameter. Running node-sass styles.scss styles.css --watch will recompile the stylesheet every time you make a change to it so that you don't have to go and run Sass yourself every time.

3) Project setup

To show off the features of Sass, I'm going to throw together a super simple web page using old fashioned HTML and CSS. We will then convert our styles to be in Sass which will show off how much more maintainable our styles become.

I'm not going to walk you through the process of writing this, but I do want to provide you the code so you can see what we are starting from.

If you would prefer, this code is available here on github.

My directory looks like this:

C:\Projects\sass_sample_app [master ≡]> ls


    Directory: C:\Projects\sass_sample_app


Mode                LastWriteTime         Length Name  
----                -------------         ------ ----
d-----        8/11/2016   1:52 PM                styles  
-a----        8/11/2016   2:17 PM           3394 index.html
-a----        8/11/2016   2:34 PM             73 README.md


C:\Projects\sass_sample_app [master ≡]> ls styles


    Directory: C:\Projects\sass_sample_app\styles


Mode                LastWriteTime         Length Name  
----                -------------         ------ ----
-a----        8/11/2016   2:31 PM           1053 styles.css


C:\Projects\sass_sample_app [master ≡]>  

Here's index.html:

<!doctype html>  
<html>

<head>  
  <title>Sass Sample App</title>

  <!-- Style dependencies. -->
  <link rel="stylesheet" href="styles/styles.css" />

  <!-- Fonts from Google Fonts. -->
  <link href="https://fonts.googleapis.com/css?family=Lora|Open+Sans|Open+Sans+Condensed:300" rel="stylesheet">
</head>

<body>  
  <div class="navbar">
    <a class="navbar__brand" href="#">DCS Innovation Labs</a>
  </div>

  <div class="alert">
    This is an alert!
  </div>

  <div class="container">
    <h1>Sass Sample App</h1>
    <p>
      Lorem ipsum dolor sit amet, paulo partiendo nec ad, at usu quod putant. Semper graecis explicari eu vim. Quidam co
nsulatu ei his, duo essent tritani volumus ad. Sed ea soleat dolores, posse zril eum in. Sit ea case lobortis, sit possi  
t eripuit ex. Ex nec solum offendit. An cum soluta facilisi senserit.

      Feugiat contentiones definitionem ex ius, vim commodo fabellas no. His te habeo sensibus, vix ex cibo omnesque. Me
lius iracundia expetendis ei his, quo vero idque dicant no. Nec ex utinam altera praesent.

      Iisque docendi ne vim, sumo erat facilisi eos et. Nam corrumpit tincidunt delicatissimi no. Tritani petentium inco
rrupte cum no, eos no soleat feugiat luptatum. Nec mundi oporteat mnesarchum ex, duo molestiae tincidunt te, usu numquam  
 accusata cu.

      Sed cu viris legimus meliore, no malis vivendo scripserit nam. Vis no nemore docendi, vis te vidit pertinacia. His
 etiam dolorum dignissim ut. Iuvaret recusabo mel et. Id quem soleat sed, vivendum persecuti in sed, eripuit assueverit
ne eam. Agam praesent an vix.

      Vix sonet mentitum no, mel id viderer facilis. Habeo noster nec ei, alia doctus latine sed ei, te eos enim wisi co
nsul. Ad quo habemus commune sensibus, te sumo possit duo, ad offendit tacimates tincidunt cum. Cum neglegentur contenti  
ones id, in pro vocent alienum vituperata, error graece eu duo.  
    </p>

    <p>
      Lorem ipsum dolor sit amet, paulo partiendo nec ad, at usu quod putant. Semper graecis explicari eu vim. Quidam co
nsulatu ei his, duo essent tritani volumus ad. Sed ea soleat dolores, posse zril eum in. Sit ea case lobortis, sit possi  
t eripuit ex. Ex nec solum offendit. An cum soluta facilisi senserit.

      Feugiat contentiones definitionem ex ius, vim commodo fabellas no. His te habeo sensibus, vix ex cibo omnesque. Me
lius iracundia expetendis ei his, quo vero idque dicant no. Nec ex utinam altera praesent.

      Iisque docendi ne vim, sumo erat facilisi eos et. Nam corrumpit tincidunt delicatissimi no. Tritani petentium inco
rrupte cum no, eos no soleat feugiat luptatum. Nec mundi oporteat mnesarchum ex, duo molestiae tincidunt te, usu numquam  
 accusata cu.

      Sed cu viris legimus meliore, no malis vivendo scripserit nam. Vis no nemore docendi, vis te vidit pertinacia. His
 etiam dolorum dignissim ut. Iuvaret recusabo mel et. Id quem soleat sed, vivendum persecuti in sed, eripuit assueverit
ne eam. Agam praesent an vix.

      Vix sonet mentitum no, mel id viderer facilis. Habeo noster nec ei, alia doctus latine sed ei, te eos enim wisi co
nsul. Ad quo habemus commune sensibus, te sumo possit duo, ad offendit tacimates tincidunt cum. Cum neglegentur contenti  
ones id, in pro vocent alienum vituperata, error graece eu duo.  
    </p>

    <button class="btn">A wild button appeared!</button>
  </div>
</body>  
</html>  

And here's styles/styles.css:

/* Boilerplate */
*, *:before, *:after {
  box-sizing: border-box;
}

html, body {  
  color: #333333;
  font-family: 'Open Sans', sans-serif;
  height: 100%;
  margin: 0;
  padding: 0;
}

h1, h2, h3, h4, h5, h6 {  
  font-family: 'Open Sans Condensed', sans-serif;
}

p {  
  font-family: 'Lora', serif;
}

/* Helpers */
.container {
  margin-left: auto;
  margin-right: auto;
  width: 900px;
}

/* Components */
.alert {
  background-color: #D6FFAA;
  border: 1px solid #A3CC77; /* #D6FFAA darkened 20% */
  margin: 20px;
  padding: 20px;
  position: fixed;
  right: 0;
  width: 300px;
}

.btn {
  background: none;
  border: 1px solid #CC37A7; /* #FF6ADA darkened 20% */
  padding: 10px;
}

.btn:hover {
  cursor: pointer;
}

.navbar {
  background-color: #FF6ADA;
  border-bottom: 1px solid #CC37A7; /* #FF6ADA darkened 20% */
  height: 40px;
}

.navbar__brand {
  color: #FFFFFF;
  font-family: 'Open Sans Condensed', sans-serif;
  font-size: 1.2rem;
  height: 40px;
  line-height: 40px;
  margin-left: 10px;
  text-decoration: none;
}

Before we get started, we need to change the name of our file from styles.css to styles.scss so Sass will compile it.

C:\Projects\sass_sample_app\styles [master ≡]> mv styles.css styles.scss  
C:\Projects\sass_sample_app\styles [master ≡ +1 ~0 -1 !]> ls


    Directory: C:\Projects\sass_sample_app\styles


Mode                LastWriteTime         Length Name  
----                -------------         ------ ----
-a----        8/11/2016   2:31 PM           1053 styles.scss


C:\Projects\sass_sample_app\styles [master ≡ +1 ~0 -1 !]>  

Let's hop in and see how we can improve this stylesheet using Sass.

4) Using variables

I've found variables to be by far the most useful feature in Sass. Not only can they be used to reduce the workload for replacing all instances of the same value in a stylesheet, but they can also be used to make an application's styles massively configurable.

There are a few prime targets in every application that should be converted to variables:

  1. Colors
  2. Font stacks
  3. Heights of elements with height dependent children

Starting with the colors, we have:

  • ColorTextDark: #333333
  • ColorTextLight: #FFFFFF
  • ColorBrandPrimary: #FF6ADA
  • ColorBrandPrimaryDark: #CC37A7
  • ColorBrandSecondary: #D6FFAA
  • ColorBrandSecondaryDark: #A3CC77

For font stacks, we have:

  • FontStackHeader: 'Open Sans Condensed', sans-serif
  • FontStackUi: 'Open Sans', sans-serif
  • FontStackBodyCopy: 'Lora', serif

For element heights, we have:

  • HeightNavbar: 40px

Do define a variable in SCSS, place a $ followed by the variable name followed by a :. For example: $color-text-dark: #333333;.

After using variables instead of hardcoded values, our stylesheet looks like this:

// Colors
$color-text-dark: #333333;
$color-text-light: #FFFFFF;
$color-brand-primary: #FF6ADA;
$color-brand-primary-dark: #CC37A7; // #FF6ADA darkened by 20%
$color-brand-secondary: #D6FFAA;
$color-brand-secondary-dark: #A3CC77; // #D6FFAA darkened by 20%

$font-stack-header: 'Open Sans Condensed', sans-serif;
$font-stack-ui: 'Open Sans', sans-serif;
$font-stack-body-copy: 'Lora', serif;

$height-navbar: 40px;

/* Boilerplate */
*, *:before, *:after {
  box-sizing: border-box;
}

html, body {  
  color: $color-text-dark;
  font-family: $font-stack-ui;
  height: 100%;
  margin: 0;
  padding: 0;
}

h1, h2, h3, h4, h5, h6 {  
  font-family: $font-stack-header;
}

p {  
  font-family: $font-stack-body-copy;
}

/* Helpers */
.container {
  margin-left: auto;
  margin-right: auto;
  width: 900px;
}

/* Components */
.alert {
  background-color: $color-brand-secondary;
  border: 1px solid $color-brand-secondary-dark;
  margin: 20px;
  padding: 20px;
  position: fixed;
  right: 0;
  width: 300px;
}

.btn {
  background: none;
  border: 1px solid $color-brand-primary-dark;
  padding: 10px;
}

.btn:hover {
  cursor: pointer;
}

.navbar {
  background-color: $color-brand-primary;
  border-bottom: 1px solid $color-brand-primary-dark;
  height: $height-navbar;
}

.navbar__brand {
  color: $color-text-light;
  font-family: $font-stack-header;
  font-size: 1.2rem;
  height: $height-navbar;
  line-height: $height-navbar;
  margin-left: 10px;
  text-decoration: none;
}

It's evident right away that this stylesheet is easier to maintain and easier to customize should you all the sudden want to make some color changes.

5) Using functions

Just like in a "real" programming language, functions are another great way to abstract repeated functionality. Sass has two types of functions, one being called a function, and the other being called a mixin. While very similar, there is one key difference between the two. Functions return a single value, and mixins return a single or group of style declarations. I tend not to find mixins to be terribly useful, so we'll focus on functions.

A key candidate for a function in this file is that I am using darkened variants of existing colors: $color-brand-primary-dark and $color-brand-secondary-dark. I have been guestimating these up until now, but I can create a function to do that work for me (and more accurately).

@function darkenBrand($color) {
  @return darken($color, 20%)
}

You can just call this like you would a function in any other language:

$color-brand-primary-dark: darkenBrand($color-brand-primary);

One thing to take note of here is that I am calling another function darken in my function darkenBrand. Along with allowing users to define functions, Sass also has a large library of internal functions and operations like darken. There is no way that I could go over all of these in a single blog post but Sass has great documentation on these that is definitely worth checking out.

After applying this function, our stylesheet looks like:

// Functions
@function darkenBrand($color) {
  @return darken($color, 20%)
}

// Variables
$color-text-dark: #333333;
$color-text-light: #FFFFFF;
$color-brand-primary: #FF6ADA;
$color-brand-primary-dark: darkenBrand($color-brand-primary);
$color-brand-secondary: #D6FFAA;
$color-brand-secondary-dark: darkenBrand($color-brand-secondary);

$font-stack-header: 'Open Sans Condensed', sans-serif;
$font-stack-ui: 'Open Sans', sans-serif;
$font-stack-body-copy: 'Lora', serif;

$height-navbar: 40px;

/* Boilerplate */
*, *:before, *:after {
  box-sizing: border-box;
}

html, body {  
  color: $color-text-dark;
  font-family: $font-stack-ui;
  height: 100%;
  margin: 0;
  padding: 0;
}

h1, h2, h3, h4, h5, h6 {  
  font-family: $font-stack-header;
}

p {  
  font-family: $font-stack-body-copy;
}

/* Helpers */
.container {
  margin-left: auto;
  margin-right: auto;
  width: 900px;
}

/* Components */
.alert {
  background-color: $color-brand-secondary;
  border: 1px solid $color-brand-secondary-dark;
  margin: 20px;
  padding: 20px;
  position: fixed;
  right: 0;
  width: 300px;
}

.btn {
  background: none;
  border: 1px solid $color-brand-primary-dark;
  padding: 10px;
}

.btn:hover {
  cursor: pointer;
}

.navbar {
  background-color: $color-brand-primary;
  border-bottom: 1px solid $color-brand-primary-dark;
  height: $height-navbar;
}

.navbar__brand {
  color: $color-text-light;
  font-family: $font-stack-header;
  font-size: 1.2rem;
  height: $height-navbar;
  line-height: $height-navbar;
  margin-left: 10px;
  text-decoration: none;
}

And our compiled CSS looks like:

/* Boilerplate */
*, *:before, *:after {
  box-sizing: border-box; }

html, body {  
  color: #333333;
  font-family: "Open Sans", sans-serif;
  height: 100%;
  margin: 0;
  padding: 0; }

h1, h2, h3, h4, h5, h6 {  
  font-family: "Open Sans Condensed", sans-serif; }

p {  
  font-family: "Lora", serif; }

/* Helpers */
.container {
  margin-left: auto;
  margin-right: auto;
  width: 900px; }

/* Components */
.alert {
  background-color: #D6FFAA;
  border: 1px solid #a5ff44;
  margin: 20px;
  padding: 20px;
  position: fixed;
  right: 0;
  width: 300px; }

.btn {
  background: none;
  border: 1px solid #ff04c1;
  padding: 10px; }

.btn:hover {
  cursor: pointer; }

.navbar {
  background-color: #FF6ADA;
  border-bottom: 1px solid #ff04c1;
  height: 40px; }

.navbar__brand {
  color: #FFFFFF;
  font-family: "Open Sans Condensed", sans-serif;
  font-size: 1.2rem;
  height: 40px;
  line-height: 40px;
  margin-left: 10px;
  text-decoration: none; }

This leaves our glorious website to look like:
Sass Sample App Screenshot

You're up and running!

This post really just hits the tip of the iceberg on the capabilities of Sass. If you would like to learn a bit more, a great resource is the Sass Basics guide. If you want to get into the nitty gritty, the Sass Documentation is excellent. You can create some truly magical things that simply wouldn't be feasible with CSS with the power that Sass gives you.

With that said, the one killer feature for me in Sass is variables, followed closely by functions. They have changed the way that I write my styles and have proven invaluable in maintaining larger projects.

I hope that this post was helpful for you and that you will try out Sass for yourself on your next project!