Sass 3.4 is Out!

Share this article

Only a couple of months after Sass 3.3, version 3.4 has already shipped under the code name “Selective Steve”. Quite an odd name but we’ll see that the name is pretty accurate since most new features are about selectors.

The Parent selector & in SassScript

“In SassScrwhat?” you ask. You can think of SassScript as the language of Sass. Sass is the eco-system, the preprocessor, while SassScript is what makes Sass, well… Sass, under the hood. Or as Natalie Weizenbaum puts it:

“SassScript” is what we call the mini-language Sass uses for variables, property values, and so forth. It’s mostly just CSS values, but it also supports custom functions, arithmetic, and so forth.

Before Sass 3.4, you couldn’t do much with the parent selector (although I hate this name since it’s not a parent selector but a reference to the current selector, which is utterly different but whatever), except things like:

.selector {
  .no-svg & {
    // Stuff
  }

  &:hover {
    // (H)o(v|th)er stuff (see what I did there?)
  }
}

Well, now we can do much more. At first, this new feature was supposed to come with Sass 3.3 but it got delayed because of technical issues.

Basically, you can now manipulate & as you can any other variable. It always contains a list of lists, as you would expect it to.

For instance, if you have a selector like .foo .bar, .baz, then the upper list for & would contain 2 items: .foo .bar and .baz, both of them being lists. Then, the first contains 2 items as well (.foo and .bar), while the second only contains one (.baz).

Beware, the SassScript representation of a selector made of a single part is still a 2-level deep list. For instance .item would be ((item,),). So if you want to grab .item from &, you’d have to run nth(nth(&, 1), 1) (first item of the first item of &).

Okay so it’s probably not quite obvious why we would need something like this. Admittedly, while it’s true we’ve been doing without this for a while now, there is one specific thing — which happens quite frequently actually — that we couldn’t do until now:

// This doesn't work in 3.3.
// This doesn't work in 3.4.

.selector {
  color: blue;

  a& {
    color: red;
  }
}

The typical scenario is: I want to qualify a selector on the fly. In our example, we have .selector to which we want to apply custom styles if it’s an anchor (a).

It’s still not as simple as that, but at least we can do it now:

// This doesn't work in 3.3.
// This does work in 3.4.

.selector {
  color: blue;

  @at-root #{a + &} {
    color: red;
  }
}

First, we need to output it at root level, thanks to @at-root, or we’ll end up with something like .selector a.selector which is not what we want. Then, we just need to interpolate the whole thing.

Now, we could make things a bit more modular by making a function for this. What about something like this:

// This doesn't work in 3.3.
// This does work in 3.4.

@mixin qualify($selector) {
  @at-root #{$selector + &} {
    @content;
  }
}

And then:

.selector {
  color: blue;

  @include qualify(a) {
    color: red;
  }
}

Pretty cool, isn’t it? As far as I am aware, this is one of the only simple use cases for & in SassScript. There are probably plenty of other scenarios, but they usually involve a much more complex context (Sass frameworks, etc.).

Selector Functions

To go along with this brand new toy that is SassScripted parent reference selector, and to stick to the Selective Steve name a little more, you’ll be pleased to know Sass 3.4 is bringing a handful of new functions to let you play with selectors.

I won’t go too deep into those functions since they will be useless for most developers (including me). Indeed, this feature is specifically targeted to framework developers (which has been confirmed to me by Natalie on Twitter, although I can’t find the tweet because Twitter’s search is lame as hell).

Basically, you have a function to do anything you want; whether it be appending a selector to another selector, nesting a selector under another selector, replacing a selector within another selector, unifying selectors, and much more.

Nevertheless, I’d like to talk about two of those functions: selector-extend and selector-replace.

First, it is worth pointing out that selector-extend works in the exact same way as the @extend directive. In fact, I suspect that’s what’s being used by the directive under the hood.

Secondly, the selector-replace function is actually much more efficient than one would first think. Warning! It is not a string replacement function. It actually makes use of the @extend directive power to replace selectors without breaking the logic that was put there in the first place.

That is — and here I’m shamelessly copy-pasting Natalie’s example — selector-replace(".foo.bar.baz", ".foo.baz", ".qux") (replacing .foo.baz by .qux in .foo.bar.baz) returns .bar.qux. A regular string replacement function would have not returned anything since .foo.bar doesn’t exist as a string in .foor.bar.baz. This is where Sass selector functions are kicking in.

Anyway, if you ask me, most of those functions are close to useless in any reasonable project. Some folks have found use cases, for example (Marc Mintel here and Micah Godbolt here), but as far as I’m concerned I think these doesn’t solve much, and when they do, it adds too much code complexity. Keep Sass Simple!

Error Handling with @error

Okay, enough with the selectors already! If there is one thing I was awaiting in Sass 3.4, it’s the @error directive. Actually, as a framework developer, it’s been almost two years that I’ve been waiting for such a feature!

The days of @warn "..."; @return null; are over, my friends. Long live @error! Until now, Sass never had a clean way to handle errors. If you’ve been a long-time reader of my articles, you may be familiar a pattern like the following:

@function rainbow($unicorn) {
  @if type-of($unicorn) != "unicorn" {
    @warn "What?! `#{$unicorn}` is not a unicorn? Are you serious?!";
    @return null;
  }

  // Proceed with the code.
}

So basically, we had to check whatever we wanted to check, warn the user through @warn, return null (or false or whatever) and then deal with this in all the logic we could have.

Let’s rewrite our code with the magic of Sass 3.4:

@function rainbow($unicorn) {
  @if type-of($unicorn) != "unicorn" {
    @error "What?! `#{$unicorn}` is not a unicorn? Are you serious?!";
  }

  // Proceed with the code.
}

Boom. If the parser reaches the @error directive, it will simply crash and print the error message in the console, as if you had a syntax error or anything else. The code just stops. This is great!

Some Improvements Regarding Colors

First, the new rebeccapurple color (#663399) has been added and is now treated as any other color in Sass. This, as a tribute to (CSS Legend) Eric A. Meyer’s daughter, whose favourite color was purple, and who recently passed away at the age of 6.

In addition, there have been some tiny yet nice improvements to the way Sass handles colors. For instance, Sass will try to preserve the way colors are authored when possible, except in compressed mode where it will try to find the most compact way to represent colors.

Also, rgb(), rgba(), hsl(), and hsla() functions now clamp their value to minimum/maximum authorized values instead of throwing an error. This change has been made in order to stick to official CSS specifications from CSS Colors Level 4.

Finally, and because there has been a lot of complaining about this, Sass will now display a warning when a color is used in interpolation (#{}). Basically, some developers were voicing their displeasure with the result of code like the following:

@each $color in darkolivegreen, firebrick, blanchedalmond {
  .item-#{$color} {
    color: $color;
  }
}

While this produces the expected result in any mode but compressed, when compiling Sass for production, the resulting CSS is:

.selector-#556b2f{color:#556b2f}.selector-#b22222{color:#b22222}.selector-#ffebcd{color:#ffebcd}

As you can see, color names have been replaced by their hexadecimal equivalents because they are shorter, which can save some bytes. The solution is to quote the colors in the list ("darkolivegreen", "firebrick", "blanchedalmond"), but to prevent any more confusion, Sass will warn about this directly in the console.

Are You Not Entertained?!

Are you entertained?

If all of this is not enough for you, well worry not, my friend, because there is much more to discover about Selective Steve (i.e. Sass 3.4).

Let me try to entertain you a little more with a couple of extra features I deem interesting. Real quick:

  • You can now remove multiple keys at once with the map-remove function. This is now consistent with map-merge, which accepts several pairs as well.
  • The @extend directive now works properly with pseudo-classes, which was not the case until 3.4.
  • Sourcemaps have been greatly improved.

Finally, be sure to read the official changelog for more information on all of the above.

Kitty GiraudelKitty Giraudel
View Author

Non-binary trans accessibility & diversity advocate, frontend developer, author. Real life cat. She/they.

LouisLsass
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week