Initially, frontend development was all about writing HTML, CSS, and JavaScript. However, over the last decade or so, it has become more complex and ever more interesting. The field’s continuous evolution has made it increasingly critical to stay on top of the latest frontend technologies and learn how to build websites more efficiently.
In this guide, we’ll demonstrate how to use SCSS — think of it as a way to write CSS with superpowers. I’ve broken it into three sections:
If you already know the language and just want to learn how to use it in your application, feel free to jump to the third section.
Officially described as “CSS with superpowers,” SCSS (or Sass) offers a way to write styles for websites with more enhanced CSS syntax. In general, browsers do not know how to process SCSS features, such as functions, mixins, and nesting. We’ll need to convert them to regular CSS files to run them in the browser.
This is where most beginners get confused. SCSS and Sass are two syntax flavors for the same concept. The difference between them is UI.
SCSS
|
Sass
|
Stands for Sassy CSS
|
Stands for Syntactically Awesome Style Sheets
|
Similar to CSS (uses curly braces and semicolons)
|
Uses strict indentation (like Python)
|
Any valid CSS is valid SCSS
|
CSS code cannot be used as SASS
|
.scss extension |
.sass extension |
Here’s an example of SCSS:
body { background-color:#000; color:#fff; }
Below is an example of Sass.
body background-color:#000; color:#fff;
It’s up to you to choose one. Don’t stress on the decision — you can easily convert SCSS to Sass and vice versa with the sass-convert
tool.
In this article, I’ll focus on SCSS mainly for two reasons.
TL;DR: CSS is inadequate.
Don’t get me wrong: CSS is cool, but not quite cool enough. The creators of SCSS had a lot of ideas to make developers’ lives easier and decided to build them into an entirely new language. Actually, to be clear, Sass was the original version of this new language; SCSS is an improved version they created later.
Unfortunately, the features of SCSS have yet to be introduced in the CSS specs and, therefore, are not supported by browsers. To run SCSS code in a web browser, you must first convert it to CSS. We’ll discuss tools you can use for this and how to automate the process later.
For this section, I’ll assume you already have some experience with CSS. I’ll introduce features of SCSS one by one and explain the syntax of each.
The structure of SCSS follows that of CSS. First, choose one or more elements using IDs, classes, or other CSS selectors. Then, add styles.
In this example, we select the elements with button
class and add some properties. This is valid as CSS code as well as SCSS code. It’s important to note that SCSS supports all CSS properties.
.button { display:inline-block; font-size:14px; padding:4px 10px; }
Both //
(single-line) and /* */
(multi-line) comments are allowed in SCSS.
// this is a single-line comment /* this is a multi-line comment */
Now let’s go over the basic enhancements of SCSS. We’ll discuss how to compile SCSS to CSS in the final section. For now, you can use CodePen to write and test your code. Make sure you select SCSS as the preprocessor in CSS settings.
For the examples below, I’ll use a simple anecdote in which you are using SCSS while your super-cool, old-school developer grandpa struggles with CSS. Let’s get started!
Your grandpa styled his navbar like this with CSS:
nav { background-color:#333; padding:1em; } nav ul { margin:0; padding:0; list-style:none; } nav ul li { display:inline-block; }
But you can do so with SCSS, like this:
nav { background-color:#333; padding:1em; ul { margin:0; padding:0; list-style:none; li { display:inline-block; } } }
Much more organized and concise, right? You have two major advantages over your grandpa:
nav
and its children are between curly braces, so you don’t need to find them in the file or in multiple filesKeep in mind, however, that when you go much deeper with nesting, the CSS file can become much larger and browsers will need to do more work to style the elements. Try to keep the selectors shallow. For example, we can save several bytes by taking the styles of li
to the outer scope.
nav { background-color:#333; padding:1em; ul { margin:0; padding:0; list-style:none; } li { display:inline-block; } }
For larger projects, this can save a huge amount of bandwidth.
It’s important to make sure this process doesn’t create any conflicts. For example:
content { ul { li { font-style:italic; } } ol { li { font-decoration:underline; } } }
In this case, we cannot bring the styles of li
elements to the outer scope because they have different rules.
Here’s the bottom line: nesting makes code more clear, organized, and concise, but be careful not to overuse it.
&
in nestingYour grandpa shows off his magical button that changes color when you hover over it. His CSS code looks like this:
button { background-color: #535353; color: #000; } button:hover { background-color: #000; color: #fff; }
You can achieve the same effect much more easily with SCSS by using the &
character in nesting.
button { background-color: #535353; color: #000; &:hover { background-color: #000; color: #fff; } }
&
always refers to the upper selection. Below are some use cases.
.some-class { &:hover { /* when hovered */ } &:focus { /* when focused */ } & > button { /* selector equls to .some-class > button */ } &-cool { /*** Notice this! ****/ // selects .some-class-cool elements } }
Variables store data. In SCSS, you can save any CSS value (even with units) in variables. Variables are defined using the $
symbol.
Variable declaration:
$my-font: Nunito, sans-serif; $my-font-color: #ffd969; $content-width: 660px;
Variable usage:
body { font-family: $my-font; color: $my-font-color; content { width: $content-width; } }
When Sass is converted to CSS, all the variables are replaced with their original values. SCSS variables are useful to keep fonts, colors, and other values consistent thought a website or web app.
SCSS variables are replaced with values when converted into CSS variables. These conversions take place before they are served to the browser, so browsers don’t even know there were variables in the first place, they just see the values. On the other hand, CSS variables are much more powerful.
CSS-Tricks outlined several advantages of CSS variables:
Does that mean Grandpa wins? Not exactly: you can use CSS variables in SCSS stylesheets, since any valid CSS is also valid SCSS. If you’re curious about CSS variables, you can learn more here.
Some things to keep in mind:
$my-global-variable: "I'm global"; div { $my-local-variables: "I'm local"; }
Changing values is done in the same way as declaring. When you change a variable, subsequent uses will have the new value while the previous uses remain unchanged.
$color: #fefefe; .content { background-color: $color; } $color: #939393; .footer { background-color: $color; }
Here, .content
will have the background color #fefefe
while .footer
will have #939393
. Global variables won’t change unless we add the !global
modifier.
$color: #111; .content { $color: #222; // this is a new local variable background-color: $color; # 222 } .footer { $color: #333 !global; // changes the global variable }
Let’s outdo your grandfather’s CSS again, this time with mixins.
A mixin is a group of CSS declarations that can be reused. The syntax is similar to functions in JavaScript. Instead of the function
keyword, use the @mixin
directive. You can have arguments too. Calling the mixin is done via the @include
statement.
Here’s how to use mixins to position elements to absolute center:
@mixin absolute-center() { position:absolute; left:50%; top:50%; transform:translate(-50%,-50%); } .element-1 { @include absolute-center(); } .element-2 { @include absolute-center(); color:blue; }
First, we defined the absolute-center
mixin. Then, we used it in multiple blocks.
Below is an example of how to use arguments in mixins.
@mixin square($size) { width:$size; height:$size; } div { @include square(60px); background-color:#000; }
Optional arguments can be defined in the same way we declared SCSS variables.
@mixin square($width: 40px) { width:$size; height:$size; }
Instead of arguments, we can send CSS rules to the mixins. Those rules can be used in the mixin using @content
.
@mixin hover-not-disabled { &:not([disabled]):hover { @content; } } .button { border: 1px solid black; @include hover-not-disabled { border-color: blue; } }
This approach allows us to reduce the repetition of the &:not([disabled]):hover
part.
@import
and @use
)Chunking code is an important practice when creating larger apps. Your grandpa can do this by creating multiple CSS files and adding them all to the HTML document.
<link rel="stylesheet" href="/path/to/css/1"></link> <link rel="stylesheet" href="/path/to/css/2"></link> <link rel="stylesheet" href="/path/to/css/3"></link> <link rel="stylesheet" href="/path/to/css/4"></link> <link rel="stylesheet" href="/path/to/css/5"></link>
Grandpa’s process is tedious. It requires the browser to make many HTTP requests, which may slow down his website.
SCSS is much better because it enables you to combine chunked files before sending the code to the browser. That way, you only need to link only one CSS file (which is usually named something.bundle.css
).
@import
The examples below demonstrate how to chunk files and import them into one parent file using @import
.
normalize.scss
:
body { padding:0; margin:0; } body, html { width:100%; min-height:100%; }
styles.scss
:
@import 'normalize'; content { max-width:660px; // and more styles }
Assuming that both normalize.scss
and styles.scss
are in the same folder, we can import one to another, as shown above.
When using @import
, all the variables, mixins, etc. become globally accessible, which is a problem when you have complex file structures and use libraries. For this reason, using @import
is now officially discouraged.
The solution is @use
.
@use
The basic usage of @use
is the same as that of @import
.
styles.scss
:
@use 'normalize'; // other styles
Files imported with @use
are called modules. To use mixins or variables of these modules, we have to call them using namespaces. By default, the namespace is the filename (without the extension).
src/_colors.scss
:
$accent-color: #535353; @mixin dark-background { background-color:#000; color:#fff; }
styles.scss
:
@use 'src/colors'; body { color: colors.$accent-color; } .dark-region { @include colors.dark-background; }
You can also use a custom namespace using as
.
@use 'src/colors' as c; body { color: c.$accent-color; }
When _
is prepended to a file name of a SCSS file, the parser knows that it is a partial file and it is there only for importing. When importing, the _
part is optional. Note that we used src/colors
to import src/_colors.scss
.
You can do some math in SCSS without Grandpa’s CSS calc()
function. You can use +,-,/,*,%
, direct values, and variables for calculations.
$content-width: 600px; content { width:$content-width; } .inner-content { width: $content-width - 60px; // substraction } .outer-content { width: $content-width + 60px; // addition }
There are four types of flow control rules: @if
/@else
, @each
, @for
, and @while
.
@if
and @else
– are similar to if
and else
in JavaScript.
// ex: using in mixins @mixin theme($is-dark: false) { @if $is-dark { // styles for dark } @else { // styles for light } }
@each
is similar to for of
in JavaScript.
// creating automated $sizes: 40px, 50px, 80px; @each $size in $sizes { .icon-#{$size} { font-size: $size; height: $size; width: $size; } }
Note: The #{$size}
notation is used to make dynamic property names and selectors using variables. This is called interpolation.
@for
is similar to for
loops in JavaScript.
@for $i from 1 through 4 { .bubble-#{$i} { transition-delay: .3 * $i; } }
@while
(not often used) is similar to while
loops in JavaScript.
By this point, you’ve learned the definition and history of SCSS, how it works, the difference between SCSS and Sass, and the SCSS language syntax. Now it’s time to actually create something to prove that your SCSS is better than your grandpa’s CSS.
In this part, we’ll demonstrate two ways to compile Sass into CSS.
Before we get started, we have to install the sass
command line, which is the easiest tool to preprocess CSS. If you’re using npm, use the following code.
npm i -g sass
Check the installation guide for some additional ways to install the sass
command line.
With the sass
command line, you can parse both .scss
and .sass
files into .css
. It automatically detects SCSS and Sass files by extension and uses the correct parser.
sass source.scss destination.css
To demonstrate the sass
command line and to review the concepts outlined above, let’s design a simple blog with one component: content (we’ll skip the header and footer to make it clearer).
First, create a folder on your local machine (I named it my-blog
) and navigate there.
cd /path/to/my-blog
Then, create two more folders.
mkdir source build
We’ll place the .scss
files in the source
folder and preprocessed .css
files in the build
folder.
Next, create an index.html
file in the root, open it in your favorite text editor, and add some HTML code.
<!-- index.html --> <html> <head> <link rel="stylesheet" href="build/index.css"> </head> <body> <content> <content> Blog content goes here </content> </content> </body> </html>
Now it’s time to create our new SCSS files. Our main file will be source/index.scss
, and it will include other files.
To start writing SCSS, we’ll set up a watcher to compile source/index.scss
into build/index.css
in real time. Use the --watch
option in the sass
command line.
sass --watch source/index.scss build/index.css
Finally, create the chunked SCSS files.
// index.scss @use 'normalize'; @use 'Content'; @use 'Footer'; body { background-color:#fafafa; font-family: Segoe UI, sans-serif; } // _normalize.scss body, html { margin:0; padding:0; } * { box-sizing:border-box; } content { display:block; } // _colors.scss $content-box: #fff; $footer: #222; $footer-font: #fff;
To recognize them easily, I name components’ style files in the Content.scss
format.
// Content.scss @use 'colors' as colors; body > content { padding:40px; margin:auto; width:660px; max-width:100%; & > content { background-color: colors.$content-box; min-height:600px; // to look cool border-radius:20px; box-shadow:10px 10px 40px rgba(0,0,0,0.06); padding:40px; } }
Now that we’ve created a basic blog page, let’s add some media queries.
Let’s say we need to remove the padding of the parent <content>
element in mobile devices (<600px
). We can do that it in two ways.
The conventional CSS way is to add media queries globally.
@media screen and (max-width:600px) { body > content { padding:0; } }
The SCSS way is to add media queries inside selectors.
body > content { @media screen and (max-width:600px) { padding:0; } }
Both methods are valid; you can use whichever you prefer. Some developers save all media queries in a single file (e.g., Mobile.scss
).
While the sass
command line is straightforward and easy to use, if you are using a bundler like webpack or Gulp, you can use plugins to parse SCSS to CSS on bundling. Check out the following guides for each bundler. One advantage of a bundler is that you can use it to parse, autoprefix, and minify at the same time.
You should now understand the basics of SCSS and how it is used in the real world. We’ve discussed the most widely used features of SCSS, but I encourage you to look over the SASS documentation for a deeper dive. Once you’re comfortable with the SCSS, you may want to look into built-in SCSS modules, which can make your life much easier when creating complex applications.
As for your super-cool developer grandpa — tell him to get with the times and switch over to SCSS.
As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.
Modernize how you debug web and mobile apps — start monitoring for free.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowFrom basic syntax and advanced techniques to practical applications and error handling, here’s how to use node-cron.
The Angular tree view can be hard to get right, but once you understand it, it can be quite a powerful visual representation.
Build a fast, real-time app with Relay 17 to leverage features like optimistic UI updates, GraphQL subscriptions, and seamless data syncing.
Simplify component interaction and dynamic theming in Vue 3 with defineExpose and for better control and flexibility.
4 Replies to "The definitive guide to SCSS"
There is no need to use sass anymore. It’s slow and obselete. Use PostCSS instead.
Thank you so much for this simple and very enlightening tutorial. I knew a basic of css and nothing of scss. Now I can compile the files and update my website easily.
Under “Using & in nesting” you show 2 examples: one in CSS that the SCSS ultimately gets converted to, and one in SCSS. You then state about the SCSS that “you can achieve the same effect much more easily with SCSS by using the & character in nesting. However, both examples are 8 lines of code and not much different. What exactly was “much easier”?
Though writing in SCSS can save you a lot of repetition in the id/class hierarchy, it is ultimately more complex since you are adding in variables, mixins, and functions that a developer needs to spend a lot of time hunting down and investigating to find out where and how to modify something. And all the SCSS is doing is writing vanilla CSS anyway — something grandpa already knows how to do without needing to learn a new language or use a CSS processor 🙂
how can I link multiple html pages in Sass..?