HTML & CSS - - By James Steinbach

Switching from Ruby Sass to LibSass

If you’ve been following the recent major announcements from the Sass community, you may have seen the huge LibSass 3.0 announcement at SassConf 2014.

For some time now, the Sass Ruby gem has been getting more advanced features, while LibSass has been updated less quickly. This means that many people using LibSass to compile their Sass (usually through node-sass in a Grunt/Gulp workflow) have been unable to use cutting edge Sass tools like the ones you keep reading about here on SitePoint.

The big announcement is that Ruby Sass will now wait for feature parity with LibSass, then they’ll be maintained at the same speed as much as possible. LibSass 3.0 is out now and you can integrate it into your task runners with the most recent node-sass version.

What makes LibSass so important, however? The biggest advantage over Ruby Sass is that it’s faster – much faster. If faster complile times sound good to you, here’s a good introduction to installing LibSass.

In this article, I’m going to be really practical: I’m going to share all the “gotchas” I discovered when I tried compiling my mixin library with LibSass. While LibSass 3.0 is close to Ruby Sass 3.4.6, there are several things that don’t work identically. You may not face all of these in your Sass, but if you do run into these little bugs, I hope this list helps you troubleshoot them.

Note: in this article, “Ruby Sass” always refers to 3.4.6 and “LibSass” always refers to 3.0.1, unless otherwise specified.

Don’t test with @if not ...

In Ruby Sass, @if not not index($list, $value) {} was a clever way to ensure that a value was in a list. I’ve been using this condition in my Ruby Sass projects since Hugo suggested it to force the condition to evaluate a true boolean value.

However, in LibSass not does not work, so this method appears to always evaluate to true. In LibSass, you can workaround this problem by using one of the following:

@if index($list, $value) { ... }
@if index($list, $value) != null { ... }
@if index($list, $value) > 0 { ... }

Check this out in this Sassmeister gist.

Watch your math syntax.

Ruby Sass will let you get away with the following:

$map: (value: 1200px);
.container {
    max-width: map-get($map,value)-1;
}
// Ruby output: 1199px
// LibSass output: 1200px -1

LibSass, however, expects you to put a space before and after the minus symbol:

$map: (value: 1200px);
.container {
    max-width: map-get($map,value) - 1;
}
//output
.container {
  max-width: 1199px; }

Check this out in this Sassmeister gist.

@error doesn’t work.

In Ruby Sass, @error prints an error to the console and stops the compiler. It also allows you to interpolate variables into a helpful error message.

However, in LibSass, the @error line will simply be printed to the compiled output as though it were a CSS declaration line. Because it’s simply being printed to output, it won’t interpolate variables if they’re inside of quotes. I suppose you could still use the broken @error directive and search your output for ‘@error’. If so, you could workaround the variable interpolation issue with string concatenation:

$error-text: 'error text';
.at-error {
  color: blue;
  @error 'Error: #{$error-text}.'
  @error 'Error: ' + $error-text + '.';
}
//output:
.at-error {
  color: blue;
  @error 'Error: #{$error-text}.';
  @error 'Error: error text.'; }

See this in action on this Sassmeister gist.

(@warn still prints a warning to your ‘console’ and keeps compiling.)

Neat grids work; Susy doesn’t work yet.

Not long ago, I wrote about switching from Neat to Susy for Sass Grids. If you’re using LibSass, however, I’d urge you to hold up on that switch. Susy doesn’t run on LibSass quite yet. However, even though Susy doesn’t work as-is, you can create your own grid system using Su.

Neat, on the other hand, just uses variables for config properties: it’s working fine on LibSass right now. See how Neat works on LibSass in this Sassmeister gist.

selector-*() functions with & aren’t supported.

If you’re one of the rare people who has found a good use case for the new selector- functions in Sass 3.4, those won’t follow you into LibSass. I don’t have a work-around to suggest for that, other than “review your nesting strategy” (which would probably remove a need for selector interpolation entirely).

The @at-root directive doesn’t work.

Ruby Sass allows you to use @at-root to move a nested selector out of its nesting context. LibSass, however, treats @at-root like a selector string and prints it to the stylesheet:

.at-root-test {
  content: 'parent';
  .nest {
    content: 'child';
    @at-root .selector {
      content: "not nested";
    }
  }
}
//output
.at-root-test {
  content: 'parent'; }
  .at-root-test .nest {
    content: 'child';
    @at-root .selector {
      content: "not nested"; }
 }

Again, the workaround for this is to refactor your nesting. Check this out on this Sassmeister gist.

Maps

Maps support is one of the biggest features added to LibSass 3. Common map functions work just the way you’d expect. You can use map-keys(), map-values(), map-has-keys(), map-get(), and map-merge() in LibSass 3.

Maps, as you’ve gathered by now, are largely on par between Libsass 3 and Ruby Sass. Enjoy using them!

Extend

Another major addition to LibSass 3 is @extend support. I’m not aware of any differences between Ruby Sass and LibSass on this now, but it’s a substantial enough upgrade that it deserves to be mentioned here.

You can see @extend working in LibSass in this Sassmeister gist.

Conclusion

The speed improvements you’ll get from switching to LibSass are huge. However, there are still a few areas where LibSass features are not on par with features Ruby Sass 3.4+. If your mixin library depends on those specific features, hold off on updating for now.

If you’ve moved to LibSass and found other issues or some other syntax tweaks or workarounds, please share those in the comments!

Sponsors
Login or Create Account to Comment
Login Create Account