HTML & CSS
Article

Sass Basics: @-rules and Directives

By Reggie Dawson

The @-Rules and directives in Sass are backbone features that you should at least know about, especially when it concerns the @import rule. Some of these @-rules are extensions of CSS @-rules while others are Sass specific directives. Either way it is best you know what these rules are for and how they work if you are going to be working with Sass.

@import

Sass extends the CSS @import rule so it can import SCSS/Sass files. Normally we use this rule to import our Sass into one master file. We include this master file as our main CSS file in a project to use all of the consolidated rules. Importing a file gives access to any mixins or variables included in the files.

Sass by default looks for other Sass files in the current directory and then in the Sass file directory under Rails. You can also specify additional directories using the load paths option. If you are using Compass you can specify the directory by changing the sass_dir value in config.rb.

@import looks for a Sass file to import but will compile to a CSS @import rule under the following conditions:

  • The file extension is .css
  • The filename begins with htp://
  • The filename is a url()
  • The @import has media queries

If the extension of the file is .scss or .sass the file will be imported. If there is no extension Sass will try and find a file that matches the name and the proper extension. For example:

@import "sample.scss";

@import "sample";

Both of these @import statements are valid and will import the sample.scss file. The second import would also import sample.sass if it existed.

We can also import multiple files in one @import statement by including the files in a comma separated list.

@import "sample1", "sample2", "sample3";

Care must be taken when importing files, as they must be imported in a correct order. For example, lets say we have a file called myVariables that includes the variables in our project. If we have another file called myStyles that uses these variables, the myVariables file must be imported first.

@import "myStyles";
@import "myVariables";

If we import in this order we will get an error as myStyles will try and use variables that have not yet been defined.

@import "myVariables";
@import "myStyles";

This is the correct way to import as the variables referenced in the myStyles file have already been imported.

Partials @import

When you import a SCSS/Sass file it will compile to a CSS file of the same name. Normally we want to import separate SCSS/Sass files into one master CSS file versus multiple CSS files. In order to accomplish this we need to @import the files as partials. We make a SCSS/SASS file into a partial by adding an underscore in front of the name. The underscore tells Sass not to compile the file as an individual CSS file, but to merge it with the current file that contains the @import statement.

When we import these partials we do not have to include the underscore. Lets say we changed myVariables into _myVariables. We would import the file the same way.

@import "myVariables";

The difference is that no myVariables.css file will be created, the file will be merged with the main CSS file. It is important to note that you cannot include a partial and non-partial with the same name in a directory. We could not have _myVariables.scss and myVariables.scss in the same location.

Nested @import

Most of the time we will have our @import at the top level of our document we can include them inside our rules. The imported rules will be nested where they were imported. For example lets say we have a file name test with the following rule

.try {
  color: blue;
}

We can nest this import in our main file.

#why {
  @import "test";
}

This will compile to:

#why .try {
  color: blue;
}

Directives such as @mixin that are only allowed at the base level of a document cannot be in files that are nested imports. By the same token you cannot nest @import inside mixins or control directives.

@media

@media behaves the same as CSS @media rules with the exception that they can be nested inside of CSS rules. When @media directives are nested in CSS rules they will bubble up to the top level of the stylesheet.

.container {
  width: 60%;
  @media (min-width: 200px) and (max-width:600px) {
    width: 100%;
  }
}

Will compile to:

.container {
  width: 60%;
}
@media (min-width: 200px) and (max-width: 600px) {
  .container {
    width: 100%;
  }
}

As you can see we get a separate rule for our media query even though it was nested inside of the .container class. We can also nest @media queries inside of another. The queries will be combined using and.

@media screen {
  .main {
    @media (max-width: 600px) {
      width: 100%;
    }
    @media (min-width: 700px) {
      width: 70%;
    }
  }
}

Which gives us

@media screen and (max-width: 600px) {
  .main {
    width: 100%;
  }
}
@media screen and (min-width: 700px) {
  .main {
    width: 70%;
  }
}

As you can see two media queries were created. Lastly we can use Sass expressions such as variables, functions, and operators for feature names and values.

$format: screen;
$mobile: 500px;
$tablet: 800px;
$desktop: 1200px;

@media #{$format} {
  .menu {
    @media (max-width: $mobile) {
      width: 100%;
    }
    @media (max-width: $tablet) {
      width: 60%;
    }
    @media (max-width: $desktop) {
      width: 30%;
    }
  }
}

As you can see we are using interpolation with our $format variable and nesting our queries. We use the different device variables to build the rest of our queries. This give us:

@media screen and (max-width: 500px) {
  .menu {
    width: 100%;
  }
}
@media screen and (max-width: 800px) {
  .menu {
    width: 60%;
  }
}
@media screen and (max-width: 1200px) {
  .menu {
    width: 30%;
  }
}

@extend

Sometimes when styling a page you may have an element that should have all the styles of another element as well as styles of its own. Lets say we had two classes for text, one regular and one for emphasized text.

.main {
  color:  black;
  font-size: 12px;
}
.emphasis {
  font-weight: bold;
}

This works well enough but in order to use emphasized text we have to do this:

<div class="main emphasis">

Remember we can use Sass to lessen the amount of typing we have to do. The @extend directive allows us to have one element inherit the styles of another.

.master {
  color:  black;
  font-size: 12px;
}
.emphasis {
  @extend .master;
  font-weight: bold;
}

We have used @extend to rewrite the styles from above, which gives us:

.master, .emphasis {
  color: black;
  font-size: 12px;
}

.emphasis {
  font-weight: bold;
}

As you can see .emphasis is included with .master as well as having styles that only apply to emphasized text. Now to use this class we just have to include the .emphasis class.

With the @extend directive we can extend complex selectors, have multiple @extends, chain @extends, and much more. I could write a whole article on @extends, for more information on @extends check out the Sass documentation.

Use @extend with caution as the outputted CSS can become bloated and because of how the directive works you could be creating some really bad CSS. Hugo has written a post on why he avoids using @extend and more in-depth post on it too.

@at-root

The @at-root directive creates rules at the root of the document instead of being nested in their parent element.

.top {
  .first {
    font-size: 12px;
  }
  @at-root {
    .second {
      font-size: 14px;
    }
    .third {
      font-size: 16px;
    }
  }
  .fourth {
    font-size: 18px;
  }
}

Which compiles to:

.top .first {
  font-size: 12px;
}
.second {
  font-size: 14px;
}
.third {
  font-size: 16px;
}
.top .fourth {
  font-size: 18px;
}

The @at-root directive works on a single line or a block of selectors. As you can see the selectors nested inside @at-root are at the root of the document. The other selectors are nested inside .top.

The @at-root directive also allows you to move outside of directives by using (without:...) or (with:...). For example.

@media print {
  .copy {
    color: black;
    @at-root (without: media) {
      width: 100%;
    }
  }
}

Which give us:

@media print {
  .copy {
    color: black;
  }
}
.copy {
  width: 100%;
}

The @at-root directive also has a without, which could allow us to create a separate style without the @media query.

@debug

The @debug directive prints the value of a Sass expression to the standard error output stream. For example:

@debug 10px + 20px;

When I save the file the output is written to the output stream of my watch command.

1 DEBUG: 30px

@warn

The @warn directive prints the value of a Sass expression to the standard error output stream.

$wrn: 20px;
@warn "#{$wrn}";

It will display the value of the expression as well as the line number of the warning.

@error

The @error directive gives the value of a Sass expression as an error, again to the standard error stream

$test: 1px;
@error "#{$test}";

This will display error, line number, and value in the standard error stream.

Conclusion

We have highlighted a number of @-Rules and Directives you may have not known about. You may not find much use for them but it is always good to know the full capabilities of your tools.

Free Guide:

7 Habits of Successful CTOs

"What makes a great CTO?" Engineering skills? Business savvy? An innate tendency to channel a mythical creature (ahem, unicorn)? All of the above? Discover the top traits of the most successful CTOs in this free guide.

No Reader comments

Recommended
Sponsors
Because We Like You
Free Ebooks!

Grab SitePoint's top 10 web dev and design ebooks, completely free!

Get the latest in Front-end, once a week, for free.