Looking Into the Future of Sass

Sass is a CSS preprocessor. This is now common knowledge. First commit in 2007 by Hampton Catlin. Now the most used CSS preprocessor. Sass has come a long way since its beginning.

In case you are not very familiar with this stuff, I highly recommend you this article on topic by the David Walsh and this one by the Chris Coyier.

In any case, I'll assume you are pretty comfortable with Sass basics. Some of you may even be hardcore Sass hackers! Okay, what if we took a blog post to look forward into the future of Sass?

Version 3.3 incoming

It's been a while Nathan Weizenbaum and Chris Eppstein are working on version 3.3. As of today, the alpha version is already available (with its bugs of course) and I'm pretty confident saying the official version will be released anytime soon (no date announced).

So what's new in this brand new version of Sass? Well, quite a number of things. Most of them aren't officially announced as part of the v3.3 so I only have to guess:

On a side note, if you have the opportunity to be at CSS Summit 2013, don't miss it. Especially since Chris Eppstein will talk about Sass 3.3 with a lot of awesome people as well.

Sourcemaps

I think the greatest feature shipping in this version 3.3 has to be sourcemaps. Actually, sourcemaps aren't specific to Sass at all. It is a language-agnostic way to map production code to original code that was authored even after compilation and minification. Here is an introduction.

Ever inspected an element in the DevTools, wanted to edit its styles just before you realize the pointed source is something like styles.css:1 (first line of your CSS file)? Yes, that sucks because you don't edit CSS files but Sass files. Plus, everything is on line 1 in your CSS file (if everything's has been done properly).

Well, that's basically where Sass sourcemaps come in handy. Instead of having styles.css:1 , you may now have component.scss:42 . Isn't it better?

Five steps to be able to use Sass sourcemaps in your workflow:

Make sure you use Chrome 28+ Make sure you use Sass 3.3 Alpha ( gem install sass --pre ) Go to chrome://flags and enable Developer Tools Experiments Open DevTools, go to General tab and check Enable source maps Open DevTools, go to Experiments tab and check Support for Sass Compile your stylesheets using sass watch --sourcemap

Done. You have now told Chrome DevTools to use your freshly generated Sass sourcemaps ( .map files) to map your development code with your production code. You can now click (file names and properties) and access your Sass files.

Important! As of today, Sass 3.3 Alpha presents some serious issues when used along with Compass 0.13. You can't use compass watch for now and have to use sass --compass --sourcemap --watch style.scss:style.css . More informations on this issue.

Improved & selector

You probably already know the reference selector ( & ). It is a reference to the current selector when used in nested rules and can't be used at root level (throws a Sass error). The simplest use case is the following:

a { color: tomato; &:hover { color: deepskyblue; } }

This is the very basics. They have made a ton of improvements to this reference selector! Let's see a couple of them.

Using & in selectors

Among several bug fixes, I believe they want to make possible to use the reference selector in more complicated selectors. Please consider the following example:

$base: ".module"; #{$base} { &#{$base}-something { /* ... */ } }

In this case, you define some kind of base selector for your module. Then want to apply styles to an element who would have the base selector class and a derived from the based selector class ( .module and .module-something ). This throws a Sass error (Invalid CSS after " &#{$base}": expected "{", was "-something {" "-something" may only be used at the beginning of a compound selector.).

In the next version of Sass, we will be able to do so by interpolating the reference selector in the selector like this (reference issue):

$base: ".module"; #{$base} { #{&}#{$base}-something { /* ... */ } }

Currently, the only workaround is to remove the dot from the base variable and explicitly use it in the selector like this:

$base: "module"; #{$base} { &.#{$base}-something { /* ... */ } }

This isn't great since it forces the pattern to use a class name since the . is hard-coded in the nested selector. Still better than nothing while we wait for the new & , right?

BEM-like syntax

One thing I'm really looking forward to is the expansion of the reference selector to allow a better BEM-like syntax. If you're not very familiar with the Block-Element-Modifier syntax, I highly recommend you these posts: MindBEMding - getting your head 'round BEM syntax.

The problem -if a problem it is- when writing BEM with Sass is you can nest anymore. Let's take a real dumb example:

.block { /* Base stuff */ } .block__child { /* Sub-element of block */ } .block--modifier { /* Variation of block */ }

This is what you want. Right? Unfortunately, you can't nest things here (there is nothing to nest whatsoever). What if we could right something like this...

.block { /* Base stuff */ &__child { /* Sub-element of block */ } &--modifier { /* Variation of block */ } }

This would be awesome. Syntactic sugar at its best. As of today, this throws a Sass error (Invalid CSS after " &": expected "{", was "__ child {" "__ child" may only be used at the beginning of a compound selector).

Thankfully, they will improve the reference selector in a way we can use this kind of syntax (reference issue). Quick note though, we will have to interpolate the reference selector as so: #{&}__child . No big deal.

Prevent & from bugging at root level

This case has been reported by Ericam from the Susy framework (reference issue). He made a mixin that uses the reference selector to draw a fallback for @media rules for Internet Explore Here is is:

@mixin respond-to($media, $ie-class:'ie') { @media (#{$media}) { @content; } // We need the parent selector here in order to mimic the @media bubbling. .#{$ie-class} & { @content; } }

The idea is simple: he calls the mixin passing it a valid value for @media , open braces and writes everything he wants inside. This will be displayed in a @media block. Moreover, he wants another block to be created using the reference to the parent selector for Internet Explorer.

// This works already. .container { @include respond-to('min-width: 30em') { color: red; } } // This doesn't work. @include respond-to('min-width: 30em') { .container { color: red; } }

Calling the mixin from within a selector works like a charm. However calling the mixin from the root level throws a Sass error (Base-level rules cannot contain the parent-selector-referencing character '&'). This is because the & selector can't be used at root level yet.

Understandable. Yet it should be usable while referencing nothing. This would make no arm and prevent Sass errors in cases like this. Hopefully this is likely they fix this problem in a future Sass release (reference issue).

Improved if() function

Have you ever heard of the if() function? It is an approximative equivalent to the well known notation variable = condition ? true : false (JavaScript, PHP...). It works the same:

$a: 10; $b: if($a > 5, blue, red); // blue

Except one tiny problem... The function doesn't have conditional evaluation. To put it simple, the function works this way:

Check condition Evaluate true result Evaluate false result Assign according result

When it should work like this:

Check condition Evaluate according result Assign according result

This looks like I'm nitpicking but it makes a big difference in situations where one of the results shouldn't be parsed. Take the following example:

@function dosomething($argument) { $second-item: if( type-of($argument) == list, nth($argument, 2), $argument ); @return $second-item; } // This works like a charm dosomething( (item-1, item-2) ); // This throws an error dosomething( only-item );

This function returns the second element of the list if the given parameter is a list or the given parameter if it isn't. At least, that's its purpose. Unfortunately, this outputs: List index is 2 but list is only 1 item long for `nth' when passing it a single value or a 1 item long list.

This shouldn't fail. The normal behavior would be to check the condition, realize it is false in the current case, so jump to the 3rd argument without even trying to parse the 2nd one. Sadly, the if() parses everything.

So they decided to fix this by making the if() a parser built-in (reference issue).

Advanced operations for lists

Sass provides a decent number of functions to manipulate lists. They are all great especially the join() function to append a list to another one. They are thinking of adding the + operator for lists (reference issue).

$a: item-1, item-2, item-3; $b: item-4, item-5, item-6; /* Same as * $c: join($a, $b); */ $c: $a + $b; // item-1, item-2, item-3, item-4, item-5, item-6

This is really just syntactic sugar but still, I think this is nice. Now, Chris even proposed to implement the - operator for lists!

$a: item-1, item-2, item-3; $b: item-3, item-4, item-1; $c: $a - $b; // item-1, item-2, item-3, item-4

The - operator should remove from the first list all the values found in the second one. This could be a great feature when you want to remove -let's say- all false and `null values from a list.

$a: item-1, false, item-2, null, item-3; /* Same as * $a: reject($a, false); * $a: reject($a, null); */ $a: $a - (false, null); // item-1, item-2, item-3

Even if this would be awesome, Nathan doesn't seem very interested in implementing the - operator since the only language where things behave like this is Ruby. But not everybody's familiar with Ruby.

To end with lists, I heard the nth() function you use to get the nth child of a list will now accept negative indexes to count from the end of the list instead of the start (reference issue). To get the last item, you will be able to do this:

$list: a b c d e; $last-item: nth($list, length($list)); // Old way $last-item: nth($list, -1); // New way

A new list-separator() function

This one is a pretty small leap forward but I think this is the nice addition to all the kind of things we can do with Sass lists.

They plan to add a native list-separator() function to determiner whether a list is separated by commas or spaces (reference issue). The usage is pretty straight forward:

$a: item-1, item-2, item-3; $b: item-1 item-2 item-3; list-separator($a) -> comma list-separator($b) -> space

The function returns either comma or space. Since every value in Sass is treated as a list, passing a single-element value will return space.

Maps support

It took me some time to hear about this one. It seems they want to add maps support to Sass 3.3 (reference issue). Maps are like associative arrays. Like a list of pairs where element A goes along element B.

map = { A: B, C: D }

This is a map. In a JSON-like language. As of today, I'm not sure I see the benefit of a map over a two-levels deep list. This is how you emulate a map in Sass with nested lists:

$map: ( A B, C D ); @each $pair in $map { $first-item : nth($pair, 1); $second-item : nth($pair, 2); }

It works great. It makes sense. That's why I don't really understand why they want to bring the map syntactic the language. Actually I can see one (good) reason: making the syntax more structured and less hacky.

In any case, this what I could find regarding the incoming syntax:

$map: ( a: b, c: d ); @for $key, $value in $map { /** * $key is what we called $first-item * $value is what we called $second-item */ }

And that's it for now. They couldn't settle things down regarding other features like map-creations, keyword arguments and such. From what I heard, only the basics will land in 3.3; remainder will come later. For more informations on Sass maps, follow this issue.

Improved @for loop

We love loops. Loops are awesome! They are a great way to reduce the amount of code required for some tasks. Sass provides the usual 3 different types of loops:

@for : let a variable i from m through n @while : while a condition is true @each : for each element in the array

They are all great. However the @for loop has a tiny issue. Have you ever tried initializing a @for loop going backward (i.e. decrementing)? Something like:

@for $i from 5 through 1 { /* Do something with $i */ }

Well don't bother, this will output nothing. Not even a Sass error. Currently, the only workaround for this stuff is doing so:

@for $i from -5 through -1 { /* Do something with abs($i) */ }

Basically, you turn both values negative, making the loop incrementing again, then you deal with abs($i) instead of $i to access the value you wanted in the first place.

Anyway, they plan on fixing this bug for the 3.3 (reference issue). Chris Eppstein would also like to add a feature to increment/decrement the value by another value from 1. It should look like this:

@for $i from 1 through 10 by 2 { /* Do something with $i */ }

However Nathan Weizenbaum doesn't seem very in agreement with complicating the @for syntax for such a borderline thing that can easily be reproduced by hand.

@for $i from 1 through 10 { @if $i % 2 != 0 { /* Do something with $i */ } }

@at-root rule

Okay, I'll be perfectly honest with you: this one is a mystery to me. The @at-root directive takes a nested chunk and move it to the root level. Fine, this is nice. But why? Why couldn't you just write it at root level in the first place?

I couldn't find much information regarding the @at-root rule except for the syntax. Here are a couple of usecases:

/* Example 1 - Sass */ .foo { @at-root .bar { color: gray; } } /* Example 1 - CSS */ .bar { color: gray; } /* Example 2 - Sass */ .foo { @at-root .bar & { color: gray; } } /* Example 2 - CSS */ .bar .foo { color: gray; } /* Example 3 - Sass */ .foo { @at-root { .bar { color: gray; } } } /* Example 3 - CSS */ .bar { color: gray; }

You know what? I won't go any further on this @at-root thingie. They seem to be pretty confident regarding the way they'll implement this feature but I don't see much use cases for this. More informations on this issue.

New string manipulation functions

While sniffing into unmerged branches of Sass code, I realized they were planning on adding quite a few functions to ease string manipulation along with the already existing quote(string) and unquote(string) (reference file). I found 5 of them:

str_length(string) : returns the length of $string

: returns the length of str_insert(string, insert, index) : insert $insert into $string at $index

: insert into at str_index(string, substring) : returns index of first occurrence of $substring in $string

: returns index of first occurrence of in str_slice(string, start_at, end_at = nil) : returns a substring from $string starting at index $start-at and ening at index $end-at

: returns a substring from starting at index and ening at index to_upper_case(string) : returns $string to upper case

: returns to upper case to_lower_case(string) : returns $string to lower case

Brand new @import

Last but not least, they plan on redoing the whole @import function from scratch to replace it with a more powerful, module-aware import mechanism. However this will definitely not land in 3.3; according to Chris, this will be the main feature of Sass 4.0. Among other things, the new version would allow:

@import-once

importing regular CSS files

namespacing

auto-looking for index.scss or index.sass when importing a folder

or when importing a folder importing all files from a folder (maybe?)

Please note this is really just speculating. I've gathered this list from opened issues related to @import . By the way, I've got rid of proposals that didn't please either Nathan or Chris.

Import once

If you're familiar with PHP or other programming languages, you'll probably know the include_once directive (with a quite similar syntax). The idea is to import a file once and only once.

In the current state of things, if you import twice the same file the compiler outputs twice the content of the imported file. Not great!

A workaround proposed by Seanofw is to use a little function appending filenames into a list, then checking filenames to make sure the files are only included once.

$imported-once-files: (); @function import-once($name) { @if index($imported-once-files, $name) { @return false; } $imported-once-files: append($imported-once-files, $name); @return true; } @if import-once("_SharedBaseStuff.scss") { /* ...declare stuff that will only be imported once... */ }

Thankfully, the new @import will be simpler to use than this function (reference issue)!

Importing regular CSS Files

One thing that can be annoying when you move a CSS project to a Sass/SCSS one is you can't import regular CSS files in a Sass stylesheet. It makes sense since it will completely crash the whole thing if you use the Sass syntax.

I think Normalize.css is the perfect example. When doing @import "normalize" , the compiler will do nothing if the only matching file is a regular .css file. So you have to convert the CSS file to a Sass/SCSS file; this sucks.

It should be possible with the new @import (reference issue).

Importing index as default

Depending on your architecture, you may manage your files in folders. Like a folder helpers containing a folder per mixin, with a single file within each. It has been proposed to implement the basic behavior of looking for a file called index when importing a folder.

So you can have something like this:

/* Current way of doing */ @import "helpers/clearfix/index"; /* New way of doing */ @import "helpers/clearfix";

Definitely not a major feature; plus I don't see the point of managing files in folders instead of direct files but still... Should be possible (reference issue).

Import all files from a folder

Same idea, pushing things further would be to be able to import all files from a folder at once. This is also called "Sass globbing" and currently exists as a Sass plugin by Chris Eppstein himself (I think this will be merged with Sass in 4.0).

This allows you to do things like this:

/* Import all files from folder helpers */ @import "library/helpers/*"; /* Import all folders from folder library */ @import "library/**/*";

However please note files are being imported in alphabetical order. This may or may not break things in your implementation.

Namespacing

One big thing missing to CSS (and in a lesser extend, Sass) is namespacing. Everything is in a the global scope. On-topic, I highly recommend you Please respect CSS global namespace by Kaelig.

Anyway, they are thinking of adding a namespace to imports (reference issue) so you can have a finer control over your files and your project.

Since this is a big feature under discussion, there is no decided syntax for namespacing files. I guess this could look like this:

@import "file"; @import "file-2" as "Module"; .element { @extend file%placeholder; @include Module.mixin(); }

This is just my idea, don't take this seriously please. If you want to contribute to this issue, please refer to this GitHub issue.

Miscellaneous

Hey, don't leave yet! Little gift, just for you. I crawled into unmerged branches of Sass code to find other pieces of cool stuff!

Unique ID

It turns out there is unique-id() function returning a unique random identifier (scoped into a Sass run) as an unquoted string. Based on what I can understand from the Ruby code, it returns a 9-characters long alphanumeric string starting with a u . Probably like u214ab34e .

I'm not quite sure what's the main purpose of such a thing in itself. However, this could be a way to get a random number in Sass I guess. Let me try...

@function parse-unique-id($value) { $letters: a b c d e f g h i j k l m n o p q r s t u v w x y z; $value: unquote(""); @for $i from 1 through str-length($value) { $letter: str-slice($value, $i, $i + 1); @if index($letter, $letters) == false { $value: str-insert($value, $letter, str-length($value) + 1); } } @return $value; } $number: parse-unique-id(unique-id());

This code snippet won't compile if you test it yet, even with the latest build since it relies on incoming features. Basically, it checks each character of the value of unique-id() : if it is a letter, it removes it.

It will give you a random number between 0 and 9999999. Do whatever you want with it. :D

Existence checks

I found a couple of functions which I believe are mostly made for internal gearing or Sass libraries (reference issue). Basically they allow you to test whether a variable, function or whatever exists in the current state.

variable_exists(named)

global_variable_exists(named)

function_exists(named)

mixin_exists(named)

There also seems to be a feature_exists(named) function which is meant to check if a feature exists in the current Sass runtime. I guess it will be used in future versions to check whether a given feature exists or not.

Final words

Okay, I guess it will be all. Please, keep in mind most of this stuff is the result of code sniffing and GitHub issue spying; no date or guarantee announced for any version/feature.

If you're interested in incoming features of Sass, I recommend you have a glance at the official repo.

Thanks for reading anyway! See you!