DEV Community

Camilo Micheletto
Camilo Micheletto

Posted on

Headstarting with Sass

If you're a brazillian portuguese speaker, lê esse aqui que tá suave.


This text is for those of you who feel that everyone around you is using Sass, except you.

Usually, anyone who wants to start learning Sass have the following questions:

What is the difference between Sass and Scss?

Sass is the language, it is an extended CSS, in its original form it is written with indented nestings just like HTML, which makes it easier to write relations between parents and children. Scss is a syntax of this language that is very similar to CSS. Both languages ​​extend the functions of CSS and are pre-processed and transformed into CSS before going to the browser. The vast majority of times you hear about Sass, the person may be talking about SCSS, because it's the most popular syntax.

Pre-processing?

Pre-processing is a routine that compiles your Sass code into CSS. It can be done with Gulp, Grunt, Webpack among others, which can be a barrier for some people who want to learn Sass but have difficulty with these automators. We will chew this difficulty using Live Sass Compiler - an extension of VSCode.

Processing Sass with Live Sass Compiler

First download the extension Live Sass Compiler from the VSCode Marketplace
The extension will search for all your Sass files and create a normal and minified CSS from them in the same folder. If you want to configure which folder you want the final files to be saved to, just go to the extension settings and access its settings.json.

Gif configuring the file output in Live Sass Compiler's settings.json file

Then just add this chunk of code to the document (or edit if it already have one). In this section you define the format of the code - whether it is expanded or minified - what the name of each extension is and what location you want to save. I leave the option generateMap as false because we usually don't use map.

"liveSassCompile.settings.formats": [ 
    {
        "format": "expanded",
        "extensionName": ".css",
        "savePath": "app / css"
    },
    {
        "format": "compressed",
        "extensionName" : ".min.css",
        "savePath": "app / css"
    },
  ],
  "liveSassCompile.settings.generateMap": false,
Enter fullscreen mode Exit fullscreen mode

If you have more than one file, you can edit the settings to disregard the files within some directories:

"liveSassCompile.settings.excludeList": [

    "** / node_modules / **",
    ".vscode / **",

    // add your folder here !!
    "** / example / **" 
  ], the
Enter fullscreen mode Exit fullscreen mode

Ideally, if you divide the style sheets by components, create a main style sheet and inside it you import all your style sheets, that way you only need to compile one single file.

Inside main.scss

@import './components/button.scss';
@import './components/reset.scss';
@import './components/variables.scss';
Enter fullscreen mode Exit fullscreen mode

Want to do it hands-on? I recommend this article that teaches you how to compile Sass with GulpI recommend this article that teaches you how to compile Sass with Gulp.

File structure

If the size of your code justifies it, it is important to create a separation of responsibilities within the style sheets so that the code is easier to maintain and rectify.
There is a standard structure of Sass in projects called 7 x 1 that separates the elements in 7 different folders that are imported into a base file, the structure looks like this:

sass /
|
| - abstracts /
| | - _variables.scss # Sass Variables
| | - _functions.scss # Sass Functions
| | - _mixins.scss # Sass Mixins
| | - _placeholders.scss # Sass Placeholders
|
| - base /
| | - _reset.scss # Reset / normalize
| | - _typography.scss # Typography rules
| … # Etc.
|
| - components /
| | - _buttons.scss # Buttons
| | - _carousel.scss # Carousel
| | - _cover.scss # Cover
| | - _dropdown.scss # Dropdown
| … # Etc.
|
| - layout /
| | - _navigation.scss # Navigation
| | - _grid.scss # Grid system
| | - _header.scss # Header
| | - _footer.scss # Footer
| | - _sidebar.scss # Sidebar
| | - _forms.scss # Forms
| … # Etc.
|
| - pages /
| | - _home.scss # Home specific styles
| | - _contact.scss # Contact specific styles
| … # Etc.
|
| - themes /
| | - _theme.scss # Default theme
| | - _admin.scss # Admin theme
| … # Etc.
|
| - vendors /
| | - _bootstrap.scss # Bootstrap
| | - _jquery-ui.scss # jQuery UI
| … # Etc.
|
`- main.scss # Main Sass file
Enter fullscreen mode Exit fullscreen mode

To better understand how this pattern works, you can read this documentation..

Sass variables x CSS variables

There are some differences between Sass variables and CSS variables:

Sass variables are static

The Sass variable after compilation becomes a static value, whereas the CSS variable remains a variable, which can be edited on the web using browser developer tools and also accessed using Javascript.

CSS variables do not work as parameters for media-queries

@media screen and (max-width: var (- small-screen)) {
  / * Wont work* /
}

@media screen and (max-width: $small-screen ) {
  / * Will work :) * /
}
Enter fullscreen mode Exit fullscreen mode

You can't use CSS variables within @media queries, but you can use Sass variables. This is useful when you want to preset variables in your breakpoints or place dynamic values ​​on them.

Sass variables are not escaped by DOM elements

: root {
 --font-size: 3em;
}

@media screen and (max-width: 432px) {
/ * In the 432px wide viewport the font-size will be 2em * /
  --font-size: 2em;
}
Enter fullscreen mode Exit fullscreen mode

In CSS, variables are scoped inside the selectors and inherited by the child selectors, whereas the Sass variables have no scope, as they are pre-processed before reaching the browser, they do not access the DOM. The syntax and usage of the two are similar:

// CSS
--btn-color: #CD4445;

// Sass
$btn-color: #CD4445;
Enter fullscreen mode Exit fullscreen mode

An interesting implementation of these scopes is to create color themes and implement them within different selectors

:: root {
  --primary: #34CED1;
  --secondary: #FCFAF7;
  - success: #505155; 
}

.dark-theme {
  --primary: #FCFAF7;
  --secondary: #505155;
  --success: #34CED1; 
}
Enter fullscreen mode Exit fullscreen mode

To implement it, just insert the class .dark-theme in the body of the document, as the class selector has greater specificity than the :root selector, the theme styles will overlap the base styles:

<body class="dark-theme" >
  <! - Variables within this class will be inherited
  by all elements of the body! ->
</body> 
Enter fullscreen mode Exit fullscreen mode

You can use Sass for more than writing nested CSS

I see a lot of people using Sass just to write fancy CSS, and I don't judge ~I do judge, but it is possible to do much better than that, especially if you write using some CSS pattern like BEM or SMACSS it is possible to nest elements within their respective blocks in a very intuitive way. Look at the example of a button made with Sass x generated CSS.

👉 Sass

.btn {
  all: unset;
  color: #FAFAFA;
  background-color: #1A1A1A;
  height: 40px;
  padding: 5px 10px;
  transition: background .6s ease-in;
  width: fit-content;
  &-red {
    background-color: #F02947;
    &:hover {
      background-color: #D63C53;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

👉 CSS

.btn {
  all: unset;
  color: #FAFAFA;
  background-color: # 1A1A1A;
  height: 40px;
  padding: 5px 10px;
  transition: background .6s ease-in;
  width: fit-content;
}

.btn-red {
  background-color: #F02947;
}

.btn-red:hover {
  background-color: #D63C53;
}
Enter fullscreen mode Exit fullscreen mode

Within Sass we can use ampersands (&) to concatenate the names of the selectors, so we can group the blocks in a more logical way. The ideal is also to separate the code by block and create nests within each block, this prevents us from making extremely complex nesting like this one:

body {
  nests/*...*/
  nav {
    /*...*/
    ul {
      / * ... * /
      li {
        /*...*/
        a {
          / * Please avoid the fuck out of this * /
        }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Ideally, the nesting should have a maximum of 3 levels, in case you need to use more than that, it is interesting to use the at-rule @at-root. This causes the selector used to be compiled at the root of the document and not nested in the code, making the CSS more readable. You can read more about @at-root in its documentation.
For anyone who wants to know more about BEM, I recommend this documentation.

Using maps, lists and loops

Of course you can nest within the .btn the modifiers --red, --blue, --green, each with their respective :hover, but there is a much more practical way of doing this - using maps. You may know maps as a Hashes or Objects, a map receives a key and a value within itself. It is also possible to save more elements using list notation.

👉 map

$font-weights: (
 "regular": 400,
 "medium": 500,
 "bold": 700,
);
Enter fullscreen mode Exit fullscreen mode

👉 list

$buttons: (
 "regular" $primary $white,
 "danger" $danger $white,
 "success" $success $white,
 "secondary" $white $darkgrey
);
Enter fullscreen mode Exit fullscreen mode

Within $buttons list, we have a string with the name of the button, the background color and the font color, so we can use that list to create the different buttons using an @eachloop, which works in a very similar way with the for in or for each of some programming languages.

This function below states that for each element in $buttons list, it will do a certain action. In this case, our list has groups of three comma separated elements. Within @each we define a variable for each element of a group in the list, so for each iteration that @each does in a group, these variables will assume different values. Remember that inside .btn we've nested modifiers with its own colors? We can do the same thing inside @each, but when concatenating selectors with variables, we use this notation #{} and pass in the variable name, in this case $name.

$buttons: (
 "regular" $primary $white,
 "danger" $danger $white,
 "success" $success $white,
 "secondary" $white $darkgrey
);

.btn {
  all: unset;
  color: #FAFAFA;
  background-color: #1A1A1A;
  height: 40px;
  padding: 5px 10px;
  transition: background .6s ease-in;
  width: fit-content;

  @each $name, $bg, $color in $buttons {
    & __ #{$name} {
       / * ... code * /
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Within this selector we can put the variable $bg as background-color and $color as color. It is also possible to nest with the pseudo-class :hover and pass a darker color using a Scss color function called darken. Darken receives two parameters - the color variable or value and the percentage by which the color should be darkened.

$buttons: (
 "regular" $primary $white,
 "danger" $danger $white,
 "success" $success $white,
 "secondary" $white $darkgrey
);

.btn {
  all: unset;
  color: #FAFAFA;
  background-color: #1A1A1A;
  height: 40px;
  padding: 5px 10px;
  transition: background .6s ease-in;
  width: fit-content;

  @each $name, $bg, $color in $buttons {
    & __ #{$name} {
      background-color: $bg;
      color: $color;
      &:hover {
        background-color: darken ($bg, 10%);
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

When using Sass' native color functions you don't need to manually declare the brightness levels of your standard colors. To learn about color functions you can look at this documentation.

You can see this complete code in my Codepen, the pattern is very similar to Bootstrap (which happened to be built with Sass).

In the same way you can build modifiers for your components dynamically using as a base your design tokens.


This post was translated by @gmartpad, one of the nicest guys i've met online, thanks bro 🤙🏽

In the next article I will talk more about functions, mixins, blocks and calculations. If this was useful to you, share it those you think this will help. Doubts, suggestions? Comment or send a message on my Twitter @lixeletto.

Discussion (0)