SASSy Node
Using SASS in a node project, for fun and profit
Why is this Important?
SASS makes CSS easier, and modularized - used correctly, you can start to mimic the MVC style of file organization you’re using on your backend (or f/e frameworks like Angular). Also, it’s a huge draw for employers. Everyone knows SASS is better than straight CSS, but very few devs have taken the time to learn and implement it in their projects. Knowing it will give you a head start in the job search!
Objectives
- Add Node/Gulp/Sass to an existing project
- Write modularized SASS for DRY styles
- Compile SASS using Node
Set Up
The starter code in this folder (app
) is the solution code from our Angular Directives Lab. It’s just a frontend, but that’s all we need to get started!
Let’s start by initializing our repo with NPM and installing Gulp.
1. Initialize Node
Hopefully this step isn’t too distant a memory - but in case you’ve forgotten, cd
into the app
folder and run npm init -y
.
2. Run your Angular project
Start a Python Simple Server to run your project:
python -m SimpleHTTPServer
3. Install Gulp
- First, cd into the app folder,
cd
into it, andnpm init -y
your project You’ll want to install gulp globally. You can do this by running thenpm install gulp -g
command. - We also need to include gulp in our project. To do that, we will run
npm install gulp --save-dev
. We use--save-dev
because a production environment should not be concerned with build tools. - Touch a
gulpfile.js
- Define our default task:
var gulp = require('gulp');
//define a task with the name of 'default'
// and a callback to perform when the task is ran
gulp.task('default', function() {
console.log('I am the default task. Hear me roar');
});
- In your terminal, run
gulp
! You should see our default task run.
4. Compile Sass
Now let's compile some Sass to CSS. We need to start by requiring the gulp-sass
plugin for Gulp.
npm install --save-dev gulp-sass
Then we can require it in our gulpfile.js
.
//gulpfile.js
var gulp = require('gulp');
var sass = require('gulp-sass');
Define a styles
task that looks in a /sass
directory for all .scss
files, logs any errors that occur, and then compiles the result into a /css
directory.
gulp.task('styles', function() {
gulp.src('sass/**/*.scss')
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest('./css/'));
});
Create a /sass
directory and touch
a main.scss
file inside.
Run gulp styles
and it will find your .scss
file and output a corresponding .css
file for you.
Lastly let's add the task we just created to gulp's default
task and have it run the task upon file changes by watching the file. Now every time you save, your .scss
will trigger the styles
gulp task and compile the changes for you!
To do this, at the bottom of your file add:
gulp.task('default',function() {
gulp.watch('sass/**/*.scss',['styles']);
});
Now we can run our Sass compilation with just gulp
.
5. What the hell, let's add Browser Sync as well
Gulp is so cool. In addition to Sass Compiling, we can give our apps other neat tricks, such as using Browser Sync
to reload our page every time a page is saved.
Start by installing Browser Sync
:
npm install browser-sync gulp --save-dev
Next, we can rewrite our gulpfile.js
to have Browser Sync
watch our file, and reload every time a page changes:
var gulp = require('gulp');
var sass = require('gulp-sass');
var browserSync = require('browser-sync').create();
// Static Server + watching scss/html files
gulp.task('serve', ['sass'], function() {
browserSync.init({
server: "./"
});
gulp.watch("sass/**/*.scss", ['sass']);
gulp.watch("*.html").on('change', browserSync.reload);
});
// Compile sass into CSS & auto-inject into browsers
gulp.task('sass', function() {
return gulp.src("sass/**/*.scss")
.pipe(sass())
.pipe(gulp.dest("./css/"))
.pipe(browserSync.stream());
});
gulp.task('default', ['serve']);
The above functions were contructed using Browser Sync's Documentation
You can stop your Python Simple Server now - Browser Sync
is running the show now! Restart gulp
and watch it go!
Refactoring with Sass
If we make a change in our main.scss
file, then save it, we should see a new file pop into our CSS folder - main.css
. Let's make that our new stylesheet, by changing the link in index.html
:
<link rel="stylesheet" href="css/main.css">
Go back to the browser, and you should see that our styles have all but dissapeared! That's because our pre-existing styles are still in style.css
. Let's move them over to our SCSS file. Remember, all CSS is valid SCSS. But we can do better! Let's take this code, and see how DRY we can make it with SASS.
Nesting
Let's start by nesting whatever style calls we can. Take a few minutes to nest the styles as deeply as you can (but, it's best practice to not go more than 4 levels deep).
Here's how I did it:
body {
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
.container {
margin: 4rem;
.row {
margin: 0;
}
}
}
header {
border-bottom: 1px solid black;
padding-bottom: 1rem;
margin-bottom: 1.618rem;
.navbar {
border-radius: 0;
}
h1 {
font-weight: bold;
letter-spacing: -2px;
max-width: 100px;
&:after {
content: "™";
font-size: 1rem;
vertical-align: super;
margin-left: 0.192rem;
}
}
}
.card {
background: #231e1e;
border-color: black;
color: white;
padding: 1rem;
min-height: 16rem;
position: relative;
border: 1px solid rgba(0, 0, 0, 0.125);
border-radius: 0.25rem;
h6 {
position: absolute;
font-size: 0.6rem;
margin-top: 1.618rem;
opacity: 0.2;
bottom: 0.618rem;
left: 1rem;
&:after {
content: "™";
font-size: 1rem;
vertical-align: super;
margin-left: 0.192rem;
font-size: 60%;
margin-left: 1px;
}
}
}
footer {
text-align: center;
margin-top: 2rem;
color: silver;
font-size: 0.6rem;
.heart {
color:#cf2e31;
}
}
Notice in the above example that I broke the page into components - body(or structure), header, cards, and footer. This will help us scale our project if and when we add new functionality.
Variables
Variables are a great way of reducing repeition in our code. Let's look for any vaules in our SCSS that are used more than once, and turn them into variables. Remember, the variable syntax for SASS is as follows:
$my-variable: value;
For starters, I see more than one place where padding has been set to 1rem. This is the perfect value to turn into a variable, because not only will it make it easier to change the value in the future, but it can serve to set up a design pattern that is easy to follow. So, let's set up a variable to define what the default padding/margin space should be:
$space: 1rem;
We've now set a system-wide standard for how to space any future objects on our page. No more guessing or eyeballing! Let's take a moment to set up that variable, and set all relevant calls to use it:
padding: $space;
DO NOT replace values of
left
andfont-size
with$space
. While it will totally work, it confuses our semantics - font size isn't a form of 'space'. If you want to set up variables for these values, do so seperately, with uniquely-identifiable names.
My SCSS looks like this now:
$space: 1rem;
body {
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
.container {
margin: 4rem;
.row {
margin: 0;
}
}
}
header {
border-bottom: 1px solid black;
padding-bottom: $space;
margin-bottom: 1.618rem;
.navbar {
border-radius: 0;
}
h1 {
font-weight: bold;
letter-spacing: -2px;
max-width: 100px;
&:after {
content: "™";
font-size: 1rem;
vertical-align: super;
margin-left: 0.192rem;
}
}
}
.card {
background: #231e1e;
border-color: black;
color: white;
padding: $space;
min-height: 16rem;
position: relative;
border: 1px solid rgba(0, 0, 0, 0.125);
border-radius: 0.25rem;
h6 {
position: absolute;
font-size: 0.6rem;
margin-top: 1.618rem;
opacity: 0.2;
bottom: 0.618rem;
left: 1rem;
&:after {
content: "™";
font-size: 1rem;
vertical-align: super;
margin-left: 0.192rem;
font-size: 60%;
margin-left: 1px;
}
}
}
footer {
text-align: center;
margin-top: 2rem;
color: silver;
font-size: 0.6rem;
.heart {
color:#cf2e31;
}
}
FUNctions
Variables are fine being used as they are, but we can also use them in functions, similar to JS. While there's a lot of insane things you can do with this power, you'll usually be using it for very simple calculations. For instance, the margin on our footer
is 2rem
, or double our $space
variable. Why not make it dynamic with a little math?
margin-top: $space*2;
Now, this is where you'll have to start making personal calls, based on your app. Do you always want margin to be double the value of padding? It's easy to paint yourself into a corner using variables and functions, so make sure you have a plan for the future of your app before you make everything dynamic.
Mixins
Mixins allow us to seperate out some of the most tedius, repetative CSS calls, and assign values to them with simple parameters. For instance, in almost every block level element, you'll find yourself declaring values for the background color, text color, and border color, like we do on .card
:
.card {
background: #231e1e;
border-color: black;
color: white;
}
What if, in the future, we wanted different types of cards, with different colors? Instead of declaring these for each value, we can write a 'colors' mixin that handles all three values for us:
@mixin colors ($background, $border, $color) {
background: $background;
border-color: $border;
color: $color;
}
Now when we start our .card
declaration, all we need to do is include the mixin, along with our values:
.card {
@include colors(#231e1e, black, white);
}
Importing partials
Taking a good look at our styles, we can start to see components growing out of our code. In fact, .card
is something that could really take on a life of it's own if any more were added. Maybe it belongs in it's own file?
In Sass, we can use @import
to seperate out our styles into semantic bits, the same way we do with our backend code in JS. Let's make a new scss file just for card styles:
touch sass/_card.scss
Notice that I added an underscore before the file name - this tells Sass it's a partial
. Partials don't get compiled into their own document, but instead are meant only to be brought into other SCSS documents raw. As a result, Gulp will ignore changes in this file and only recompile when main.scss
is saved.
Next, we can remove the entirety of our .card
declaration, and move it to card.scss
.
This is a lot neater, but it's not connected yet. Add an import declaration into main.scss
, right where the code used to be:
@import '_card';
Now whenever we save main.scss
, we'll also be importing _card.scss
into the mix.
Protip:
_card.scss
doesn't get compiled when you save it - it only gets compiled whenmain.scss
is saved by default (you don't see this in our build, though, because Browser Sync is watching EVERY file for changes). This is because it's a partial - if you hate this, remove the underscore to make it a full-blown scss document. But be warned - your site will now be making 2 HTTP requests, one for each scss document.
Extending/Inheritance
Sometimes, our classes will be so similar, that we'll want to use most (if not all) of their existing styles. With Sass, we can simply extend
our classes by mentioning them in another class:
.message {
border: 1px solid #ccc;
padding: 10px;
color: #333;
}
.success {
@extend .message;
border-color: green;
}
.error {
@extend .message;
border-color: red;
}
.warning {
@extend .message;
border-color: yellow;
}
I don't see a reason to do this in our code though . . . not yet at least . . .
Client Call!
Did you hear that telephone just now? It was our client over at Cards Against Assembly, and they have some urgent changes they need made to the site! Turns out, no one seems to know what this game is all about, so they need you to add a new card right below the header:
<section id="instructions" class="row">
<div class='instruction_card col-sm-12 col-md-12 col-lg-12'>
<h4>What is Cards Against Assembly?</h4>
<p>
Cards Against Assembly is a party game for General Assembly Students. Unlike most of the party games you've played before, Cards Against Assembly is as amazing and wonderful as you and your friends.
</p>
<p>
The game is simple. Each round, one player asks a question from a black card, and everyone else answers with their funniest white card.
</p>
</div>
</section>
Obviously, your layout wasn't built for this. But because you future-proofed your code with Sass, I bet it won't be that hard to add! Here's the client requests:
- Make it look exactly like the other
.card
s, only with inverted colors -it shoudl have a white background and a#231e1e
border and text. - Add the standard amount of margin to it.
How dry can you make the code for this new page addition? It's possible to do it with only 3 CSS rules. Look back at what we've learned in this lesson to create this new style rule with the fewest calls possible.