Tame Your CSS with Some SASS
A backend dev has been hauled away from their microservices, their exquisitely defined APIs, and their layers of abstraction… and thrown onto a front-end team to help out.
There's a crunch on, and deadlines must be met.
Our backend dev, tasked with fixing some styling issues, delves into the CSS, and horror:
Yes, magic numbers as far as the eye can see, font stacks repeated everywhere, and styles presented in an unstructured and endless parade of CSS selectors.
Modern CSS once saved us from inline HTML styles, but now it's back to eat us alive. Of course, this isn't the fault of CSS.
Sprawling CSS definitions are just a product of complex web applications with large, active teams.
But there is an excellent solution to such problems: CSS Preprocessors.
In particular, the Sass CSS preprocessor is a great option for bringing some sanity to undisciplined CSS styles, which would make our backend dev much happier.
If this sounds good to you, then read on to learn:
- How the Sass CSS Preprocessor can simplify your CSS with nesting, variables, mixins, and inheritance.
- How to install Sass and start using it on your own CSS immediately.
- How to set up a Sass to automatically process your Sass-y CSS.
This article does assume that you're at least a little familiar with CSS. If you're not, check out this article for a beginner-friendly intro to CSS, and then come right back.
So What's a CSS Preprocessor?
A CSS Preprocessor is a development tool that takes something that's better than regular CSS and spits out normal CSS you can load in a browser.
Each of these tools has its own syntax for adding features to CSS, such as:
- Nesting (or hierarchy) – structure your styling rules so that the relationship between them becomes obvious.
- Variables – no more magic numbers!
- Mixins – define a set of styles once and use it many times.
- Inheritance – one CSS rule can inherit from and “extend” another.
These are great features and they're offered by nearly all of the CSS preprocessors, but let’s take a closer look at one preprocessor in particular: Sass.
What Is Sass?
To begin with, Sass stands for “Syntactically Awesome Style Sheets,” which sounds like a justification for a cool acronym to me, but they do deliver—Sass is syntactically awesome.
It's also the most widely used CSS preprocessor. A 2016 survey found that 63 percent of web developers use Sass.
It was originally implemented in Ruby, a dependency that grated on many developers, but fortunately Sass is now a fully-fledged part of the npm (node package manager) packaging and build system.
That makes it as easy to install and use as any other npm package.
Besides that, Sass has excellent tooling support and a syntax that strikes a good balance between being concise and readable.
Finally, it's easy to learn and start using straight away.
Warning! SCSS vs Sass
There's a very confusing situation with Sass right now—it supports two quite different syntaxes. One of those syntaxes is just called Sass; the other is called SCSS.
The fact that there's the tool and a syntax called Sass doesn't really help either.
Sass syntax vs SCSS syntax is one of those things that folks like to engage in holy wars over, but I prefer SCSS because:
- SCSS is a superset of CSS3—plain CSS3 is processed successfully by Sass.
- SCSS is a bit easier to pick up if you're new to SCSS.
- It's the default syntax used in the official Sass docs, which makes life easier when you're reading up on Sass.
- It's way easier to start improving an existing project's CSS (the normal situation for me) by slowing adding SCSS rules to it. Transforming a large existing set of CSS rules into Sass would be a big job.
Having said that, you should check out the Sass syntax as well, especially for new projects. All these concepts apply to it, but it's a much more terse syntax.
So when you see some Sass styling here, it's in the SCSS syntax.
Sass Features You Can Use Right Now
Right! It's time to get our CSS in order. We're going to look at Sass's features first, and then get into installing and using it to process some sample Sass styles.
We'll start by adding some structure to our CSS:
Nesting with Sass
Here's the thing about CSS: when you're looking at a CSS file, you have to imagine the structure of the page the style rules will be applied to.
From the above, we know that there's default definition for li elements, and that there's some kind of side navigation, which is going to have li elements with anchor elements inside them.
That's easy to understand, but you need to think about how the elements are structured.
How about this instead:
Not only do we have just one entry for #sidenav, but we can see that it is going to have li elements inside it, which in turn contain anchor elements. The nested structure is a lot more intuitive.
What about media queries? They're an incredible source of duplicated CSS selectors. Well, they can be nested as well, which saves us from specifying the same selectors repeatedly:
So, no more repeating all your selectors in @media queries—now the media queries can sit right inside the selector.
But, I can totally hear you saying, what about that media query appearing over and over again, with those hard-coded max-width values?
That's going to be a maintenance nightmare.
Luckily, Sass takes care of this too, with the addition of variables.
How Variables Can Help Sass CSS
Programmers have DRY (Don't Repeat Yourself) drilled into them, and then have to use CSS, which hasn't even heard of DRY.
The introduction of variables in Sass allows us to define a value once as a variable we can use throughout our style rules. That's way more DRY.
While it's possible to redefine Sass variables, it's better to treat them like a constant, so if you need to change that value, you only need to change it in one place.
Let's see how that works by expanding on our previous example:
Here we are repeating that max-width value over and over again in our media queries. What if we want to change the screen width we start adapting our styles to? Then we'd have to change all those @media queries.
Instead of doing that, we could use a variable:
Setting the $mobile-screen variable and using it in our media queries makes our CSS much more maintainable. A nice bonus is that we also have a meaningful name applied to our max-width.
The syntax for variables is very similar to that of the normal CSS style rules:
The variable is just like a normal style rule except is has a $ at the start, and it doesn't have to appear inside a CSS selector.
Of course, we're not restricted to media query values—we can use Sass to assign colors, font-stacks, and measurements of any kind to a variable. Any value that may be repeated throughout your CSS can be replaced by a variable.
But what if you find yourself repeating groups of CSS style rules?
Mixins: Getting More DRY with Sass
We can think of mixins like a bucket of styles we want to re-use.
Let's say we have some font and border styling we'd like to apply throughout our CSS:
Sure, we can put that in a class selector and apply that class to HTML. The problem is that some elements can have competing rules that are attached to highly specific selectors, so they beat out our fancy stylings.
Here's what we can do instead:
So that’s nice; our menu items and our buttons can have standardized styling. But what we can also do is parameterize these mixins, so they work like a function:
We've added a $borderwidth parameter to our callout mixin and given it a default value of 1px.
This is great. Now we can have the same styling but configure it per selector; this combines excellent consistency of appearance with great flexibility.
Now we're getting somewhere. We've added some structure and taken advantage of variables and mixins to cut down on repetition.
What's next? Let's see how to take advantage of predefined styles to easily make new ones.
Inheritance is something normally associated with object-oriented programming (and not positively!), but in Sass CSS, it gives us an opportunity to leverage predefined styles to make new ones with very little effort.
Let's see how it works. Here's the sum of our Sass so far:
Now let's say we want a new style that combines both our existing navigation styles and the element with the border.
We can use the extend keyword in a CSS selector to inherit styles from another selector. Let's take a look:
That's a pretty short definition, but let's see what it gets us:
- The styles from #sidenav, including the li, a, and @media definitions.
- The border styles and font-stack defined in callout.
That's a lot of styling for such a little style definition.
How You Can Add SASS to Your npm Build
But how do we use Sass in an everyday development setup? The best way is to incorporate your Sass into an npm build process.
Using npm to make CSS conversions automatic will let you see your style changes straight away in a browser and is necessary for automatically deploying changes to staging and production environments.
Please note: we're going to be doing most of this from a command line. If you're not sure how to do that on your computer, now is an excellent time to learn.
If you're on Windows, the Command Prompt is an awesome and seriously old-school place to start.
On macOS and Linux, you'll want to open a terminal (Terminal application on macOS). Zed Shaw has a great mini-course on how to use a terminal in macOS and Linux right here. (He covers Windows as well, but uses PowerShell instead of cmd.exe. I think cmd.exe is more friendly for beginners.)
How to Install Node and npm
Joined at the hip, Node.js and npm come together in a package.
If you've been doing some web development already, there's an excellent chance that you have node and npm installed already.
To check, open a command prompt (cmd.exe on Windows, Terminal on macOS) and type node -v.
If you have node installed, it’ll print out the version number. Great! Head straight on to the next section.
If you don't have node installed, node -v will make your command prompt complain. Don't worry, we're going to fix that now.
For macOS and Windows, the easiest thing to do is head over to the Node downloads page, download an installer for your system, double-click the installer, and follow the instructions.
If you're on Linux, it’ll probably be easier to use your system's package manager (apt-get, yum etc). You can find instructions on doing this on Node's package manager installation page.
Once you've done that, go back to your command prompt and type node -v and npm –v for good measure. This is what mine shows:
Set up a Test Project, and Install SASS for npm
Now that you've got node and npm installed, let's set up a test directory to try out some Sass. Note that since I'm on a Mac, I'll be using commands that apply to Mac and Linux.
Make a new directory called sass-test, and change to that directory:
Now that you’re in the sass-test directory, let's initialize npm:
This command creates a package.json file, which is used by npm to keep track of what packages you're using, and what it’s supposed to do with them. The -y flag tells npm not to bother you with lots of questions about the initialization.
Now it’s time to install Sass for npm:
First of all, this produces a lot of output. Don't worry about that.
The main thing to note here is that we're installing the version of Sass that node knows how to run. The —save-dev flag indicates that node-sass should not be included in a production release—it's just used for development.
Now we've got node, npm, and Sass installed. What do we do with them?
Run Your SASS Preprocessor with npm
Let's get some Sass CSS to work with. We'll use the example Sass CSS from earlier in the article.
Under your sass-test directory, create a new directory called app, and then a directory under that called scss.
Note: scss is the extension we use for the Sass SCSS syntax. If we were using the SASS syntax, our extension would be .sass.
Save the following under the app/scss directory as app.scss:
Now, making sure you're still in the scss-test directory, run the following:
Here's what that means:
- This is how we run Sass: node_modules/.bin/node-sass
- This is the file we want to process: app/scss/app.scss
- This is where we want our CSS output to go: dist/css/app.css
- Please format our CSS nicely: –output-style expanded
So check out the dist/css/app.css file. You'll see it has regular old CSS in it that implements our Sass CSS styles.
But the command we ran to get our CSS is long. Who wants to type that in? Not me, that's for sure.
To make life easier, we'll get npm to run it for us. Edit the package.json file in the sass-test directory. It should look something like this (don't worry if it has minor differences):
In the scripts section, we'll add a command to run our Sass. Update your file so that it appears as follows (changes highlighted):
(Note the comma on the end of the first line under scripts.)
Once you've added that and saved package.json, you can now run your Sass build like this:
That's way easier! But doing that every time you make a change to your style gets kind of boring.
How about doing it automatically?
Set up a Watch to Automatically Compile Your Sass CSS into CSS
Sass has the awesome ability to automatically watch your Sass CSS files and process them whenever they change.
Let’s add another command to package.json to do that (changes highlighted again):
The important changes here involve:
- Telling Sass to watch a directory for changes (not just a file): –w app/scss
- Telling Sass where to put CSS output: –o dist/css
Everything else is the same.
Now, you can execute:
And notice how you don't get your command prompt back again. Sass is just sitting there, waiting for changes.
As an experiment, make a change to the app/scss/app.scss file, save it, and see those changes immediately reflected in the dist/css/app.css file!
Sass: Making CSS More Powerful and Less Complex
Ok, we've covered a lot of ground here, but I hope you're sold on the benefits of using CSS preprocessors, and Sass in particular.
You can make CSS more succinct and maintainable with Sass by using:
- Nesting to make the structure of CSS rules more apparent.
- Variables to make CSS more maintainable by trapping magic numbers in variables with useful names.
- Mixins to make CSS more succinct, maintainable and readable, and to capture style rules that appear together often.
- Inheritance to reuse existing styles with the @extend keyword to compose new styles based on existing ones.
Remember that backend dev we mentioned at the start, horrified by the unstructured sprawl of CSS? With Sass, they’ve gained structure, DRY code, and named abstractions. Now our dev will be much happier.