How to SASS - A Beginner's Approach

Welcome class! Now gather around, because I’m about to tell you a story… about my approach for styling with SASS, or Syntactically Awesome Style Sheets! No wait, don’t leave!

For those of you who are still here, SASS is a great way to level up your CSS, allowing you to play with mixins, pre-compile variables, nesting, explicit inheritance, and other great features. If you don’t like SASS syntax and want it to be more CSS-like, you can always use SCSS syntax, which is exactly what I did. Today I will impart upon you the methodologies I used for working with this sassy CSS. Now, onward!

Organizing with Modules

A lot of the time you will want to bundle your CSS to reduce the number of files your web app depends on. SASS supports this desire with partials, which are SASS files that are only meant to be imported and not compiled on their own; to indicate that a file is a partial, you start the file name with an underscore, _. A SASS to CSS compiler only needs one entry point, so what I do is organize my SASS into partials and then import all of the partials into one main, non-partial SASS file (which serves as the entry point). It is best to import via @use since it provides support for modules (unlike @import).

SASS actually provides their own SASS to CSS compiler. Once you have all of your partials imported into your entry point SASS file input.scss, you can create a compiled CSS file output.css using this terminal command:

sass --no-source-map input.scss output.css

In development you would typically want the compilation to occur automatically whenever a SASS file changes; in this case, use the --watch command:

sass --no-source-map --watch input.scss output.scss

If you want a source map to generate whenever your SASS compiles, just remove the --no-source-map option.

I recently completed (i.e. developed until I was no longer embarrassed of) a large web app project styled using SASS, and I ended up with a correspondingly deeply nested stylesheets directory structure. To give an idea of how you can organize your SASS partials, I will show it here:

  • stylesheets
    • codeHighlighting
    • globalCssVars
    • globalMixins
    • globalSassVars
    • views
      • components
      • layouts
      • pages

The project used Express for the backend as well as server-side rendering; as a result, a stylesheets/views folder was made to match the Express views folder. However, I would argue that without this context, the views folder here still makes sense. Components are views that are meant to be reused in pages/layouts, layouts are views that are meant to be extended by pages, and pages are, well, pages of the web app. Those are also views. Partials not in the views folder are global assets such as variables and mixins, excluding the codeHighlighting folder (this just contains a SASS theme for highlighting code snippets).

Global Variables and Mixins

Global SASS Variables

SASS variables are pre-compile variables, meaning they disappear (i.e. they are replaced by their values) once your SASS files are compiled into CSS files. These are not to be confused with CSS variables, which can also be used in SASS files and continue to exist after compilation. It should be noted that the differences between SASS and CSS variables do not end there, so do not use them interchangeably! The SASS variable is prefixed with a dollar sign, $, and are scoped by the declaration block in which they are defined. If not defined within a declaration block, the variable is scoped to its module (assuming you are importing partials via @use), which is what you will want for global SASS variables.

I try to avoid CSS variables unless a SASS variable is unable to get the job done, since SASS variables are simpler and do not have to be managed after compilation. Of course, this leads to the obvious question: “When should you use CSS variables?”. Before you can know the answer to that, you have to know what CSS variables are capable of.

Global CSS Variables

CSS variables are a different kind of beast. These variables use the double dash prefix, --, instead of the dollar sign, and they are scoped by CSS hierarchy rules instead of declaration blocks. For this reason, if you want a CSS variable to be global, declare it in the :root block. CSS variables also differ from SASS variables when it comes to reassignment. If a SASS variable is reassigned, it will only have its new value after the point of assignment. If a CSS variable is reassigned, this change cascades within its scope, updating all references to the variable that inherit its value. Since scope is CSS hierarchy, references can update before or after the point of reassignment in the file. For these reasons, CSS variables are a powerful feature, but they end up being overkill in a lot of cases.

I am certainly not an expert, but I know of some cases where CSS variables shine. One example is dynamic theming, or allowing a user to change your web app’s theme while the web app is running. Another example is JavaScript visibility: CSS variables can be retrieved and changed using JavaScript scripts, unlike SASS variables (there are posts online talking about how to export SASS variables to JavaScript, but I was unable to get these methods to work - just use CSS variables and avoid a headache).

Global Mixins

You have a bunch of CSS properties that you have noticed are often applied together. Is there a way to implement reusability in SASS? Yessir, the answer is the humble mixin. Whenever I encountered a feature that did not belong to any one view and so would be used throughout my views, I made a mixin for the feature and placed the mixin in the global mixins folder. I actually only made one global mixin, which I named cut:

@mixin cut($max-lines) {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: $max-lines;
    overflow: hidden;
}

It is used to limit the number of lines of text shown by the containing element - very useful.

The SASS + BEM Wombo Combo

When I first started building my web app, I was struck with uncertainty. What methodology should I use to create SASS code? I expected the project to be large, so I wanted to make the code as maintainable as possible. Simply diving in headfirst without a strategy would do nothing but waste time, since my development speed would be slowed now and in the future if I were to ever return to update the project. After a couple of days researching a solution, I decided to use BEM, an old but reliable methodology. This website is great for learning BEM best practices.

The idea behind BEM is to treat everything as either a block, an element, or a modifier. A block is an independent html element structure meant to be reused, an element is a dependent html element belonging to a block, and a modifier is a style that its corresponding block or element can use. There are also a couple SASS features that make SASS + BEM a great combo, which I will explain next.

Nesting Elements

In SASS you are able to nest declarations, like so:

.comment {
    .comment__author {
        font-size: medium;
    }
}

The above code would compile to a basic CSS descendant selector:

.comment .comment__author {
  font-size: medium;
}

However, SASS also has a neat feature called the parent selector, represented by the ampersand, &. What the parent selector does is simple: it refers to the outer selector. So this SASS:

.comment {
    &__author {
        font-size: medium;
    }
}

would compile to this CSS:

.comment__author {
  font-size: medium;
}

Notice how using the parent selector does not result in the CSS using a descendent selector. This feature of the parent selector, along with the parent selector itself, are time-savers for a BEM coder, since the name of a block is constantly being reused for naming elements and modifiers (well, excluding modifiers if you use shorthand naming).

Shorthand Modifier Naming

This is not exclusive to SASS, but it is still useful to know if you are going to be using BEM. In the BEM documentation, given block comment, they suggest that modifiers should be named like so:

.comment--font-large {
  font-size: large;
}

However, there is an easier way. What if I told you about this shorthand form:

.comment.-font-large {
  font-size: large;
}

Now you may be thinking: “This guy just changed a dash to a dot and thinks it’s a shorthand.” However, changing that dash to a dot changes how the CSS selector functions. Before it was “If a block or element has class comment--font-large, apply this style,” but with the shorthand form, it is now “If a block or element has both classes comment and -font-large, apply this style.” Why is this a shorthand? Well, all you have to do now to apply the style is add class -font-large to a block or element that is of class comment, whereas before you would have had to add class comment--font-large.

Obviously, you cannot do this with elements - you can only do this with modifiers because a modifier will always be directly applied to its block or element. Another block or element could have the exact same name for a modifier and the modifiers would not interfere with each other. Pretty epic.

Reusable Modifiers

Reusable? Sounds like you need a mixin! Simply place all of the modifier’s CSS properties within a mixin and then call that mixin elsewhere to reuse the modifier. If you are familiar with SASS, you may know that SASS has an extend feature (@extend), which can be used to make a declaration block inherit the style of another declaration block (so basically a simpler mixin). However, I did not use this feature once, and for good reason. You will learn why in the next section.

Media Queries

When you need to make your web app responsive, media queries are there to save the day. Or are they lurking just out of sight, waiting for a moment of vulnerability so that they can jump you? I can never remember, but either way, no one likes coding a web app and then realizing that it is not responsive when the window changes size. The best you can do is make do as much as you can without media queries, so that the additional media query code you have to add is minimal.

So what does SASS have to do with media queries? Reusability. I encountered multiple instances where I needed to change a block’s style via modifier in a media query, and you can’t add or remove classes in CSS. Too bad there is no way to take the properties from the modifier and place them in the media query without rewriting all of them. Oh wait.

Mixins. They are powerful. A good example of this is my blog post card block with modifiers -small and -smaller.  I defined the modifiers within the block, but then once I needed to apply a different modifier in a media query, I extracted the modifiers to their own file where they were placed within mixins. Then, the mixins could be imported and included using @include. Here is the modifiers file:

@mixin small() {
    .blog-post-card {
        ...
    }
}

@mixin smaller() {
    .blog-post-card {
        ...
    }
}

Here is the blog post card block file:

.blog-post-card {
    ...

    &.-small {
        @include mod.small();
    }

    &.-smaller {
        @include mod.smaller();
    }
}

@media (max-width: bp.$bp0) {
    @include mod.smaller();
}

@media (min-width: calc(bp.$bp0 + 1px)) and (max-width: bp.$bp1) {
    @include mod.small();
}

So why not use @extend? There are a few reasons. First, you cannot use the parent selector in an extend statement. Second, even if you replaced the parent selector with the parent it refers to, you cannot extend compound selectors. This means that you cannot do this:

.blog-post-card {
    ...

    &.-small {
        ...
    }

    &.-smaller {
        ...
    }
}

@media (max-width: bp.$bp0) {
    .blog-post-card {
        @extend .blog-post-card.-smaller;
    }
}

@media (min-width: calc(bp.$bp0 + 1px)) and (max-width: bp.$bp1) {
    .blog-post-card {
        @extend .blog-post-card.-small;
    }
}

Finally, and this is probably the most important reason, you cannot use extend within a media query. Why? I don’t know, but this realization certainly soured my opinion of @extend. Just use mixins and everything will be alright.

Conclusion

In this class session, you learned about how to approach SASS regarding these concepts:

  1. Organizing with Modules
  2. Global Variables and Mixins
  3. The SASS + BEM Wombo Combo
  4. Media Queries

RIIIIINNNNNNNGGGGG!!!! Oh, was that the school bell? Great, now that I’m off the clock I no longer have to pretend to care about your education. *Jumps out the window*

Comments