Home

Sass Blog

  • 
  • 
  • Dart Sass is in Beta

    Posted by Natalie Weizenbaum about 8 hours ago

    Last weekend was three days long and the weather in Seattle was gorgeous. Contrary to stereotype, spring here is often characterized by bright sunny days that aren't too hot, and on days like that I love to curl up on the armchair in my living room and write some code. This weekend, that meant finishing up the last few outstanding @extend bugs, finally making Dart Sass fully sass-spec compatible1.

    This is the milestone we've decided would mark the transition from alpha to beta releases of Dart Sass. Dart Sass 1.0.0-beta.1 is up now on npm, pub, and Chocolatey, and I encourage people to start trying it out in their own applications. We've fixed all the bugs we know about, so now we need our diligent users to find the rest of them and tell us!

    Next Steps: Ruby Sass

    There are a number of intentional behavior differences between Dart Sass and the existing implementations. All of these differences are things we think improve the language, and many of them have also made Dart Sass much easier to implement, but we recognize that they can make migration more difficult. That's why our next priority is updating Ruby Sass by deprecating old behavior or adding new behavior, as necessary.

    Our long-term compatibility goal is to ensure, as much as possible, that if a stylesheet compiles without warnings on Ruby Sass, it will also work with Dart Sass. So a substantial portion of our effort in the near future be spent on ensuring all the compatibility issues are fixed. Once that's done, we'll release those changes as part of Ruby Sass 3.5.

    Next Steps: Dart Sass

    On the Dart front, we have a number of issues outstanding that we want to resolve before we release a stable version of 1.0.0. The majority of these issues are focused on one thing: compatibility with the node-sass render() API. This will make it easy to integrate Dart Sass into existing JS ecosystem tools and workflows, since anything that works with node-sass will automatically work with Dart Sass as well.

    Try It Out

    As with all Dart Sass releases, 1.0.0-beta.1 is available on many platforms. Give it a try on whichever is easiest for you:

    • Standalone tarballs are available on GitHub, which you can just download and run from the command line.

    • Chocolatey users on Windows can just run choco install sass --pre (or choco upgrade sass --pre if you already have it).

    • You can get the pure-JavaScript version from npm by running npm install -g dart-sass.

    • Or if you're a Dart user, you can run pub global activate sass.

    I'm very pleased to have 1.0.0-beta.1 tagged and out in the world, but the work of a language maintainer is never done. I'm back to work, and if I hustle, hopefully I'll be writing about 1.0.0-rc.1 soon!


    1. Technically there are still two specs marked as "TODO". These test UTF-16 support, which is currently blocked on Dart support. ↩

  • Sass and Browser Compatibility

    Posted by Natalie Weizenbaum 4 months ago

    One of the core design principles of Sass has always been to understand CSS as little as possible. As a CSS preprocessor of course we have to understand the syntax of CSS, but as much as we can we try to avoid caring about the semantics—the meaning behind the styles. This means that Sass has no idea which properties are valid, which HTML elements actually exist, or even to a large extent what the syntax of most @-rules is.

    We get a lot of benefit from this. The less built-in knowledge Sass has about CSS, the less likely it is to work poorly with new CSS features. Imagine having to file a feature request every time you want to use a new CSS property—that would suck! Instead, older versions of Sass will happily keep working unless the actual syntax changes, which is much rarer.

    Because of this decoupling, we've never needed to worry much about browser compatibility. Sass just passes whatever CSS its given on through. It's up to the user to determine what works where, which gives them a lot of flexibility and gives us designers one fewer tough decision to make.

    But despite this general policy, there are always a few cases where CSS knowledge turns out to be necessary. A big one is @extend, which needs to know a lot about the meaning of selectors to properly unify them and weed out duplicates. Property values sometimes require semantic knowledge as well—we have to know how to interpret colors, for example.

    One of those cases has leapt up to bite us. Long ago, we made the decision to always emit transparent colors as the keyword transparent, because it was supported on IE6 through 8 and the alternative rgba() syntax was not. But it turns out that the opposite is true for more recent versions: in IE10, :hover styles aren't triggered for elements with background-color: transparent but they are with background-color: rgba(0, 0, 0, 0). Thanks, IE!

    So we were faced with a dilemma. Keep the existing behavior which was compatible with old outdated browsers that no one uses, or choose a new behavior that works better with modern browsers? The choice was pretty clear: we decided to always emit rgba(0, 0, 0, 0).

    In addition, we wanted to come up with a general rule to guide us in determining which browsers we were willing to consider outdated, and which we would continue to support (whatever that meant for the behavior in question). We decided that if a change would negatively affect less than 2% of the global market share of browsers according to StatCounter GlobalStats, we were willing to make it.

    This limit isn't set in stone. We reserve the right to change it in the future, and to make individual decisions that may affect more browsers. But this is the general guideline we're paying attention to, and we wanted you all to know.

  • Dart Sass is On Chocolatey

    Posted by Natalie Weizenbaum 5 months ago

    One of the quieter benefits of moving to Dart is how easy it is to distribute Dart applications. The Dart VM is able to bundle all the sources for an application into one easy-to-load binary snapshot, which means running a Dart application requires only three files: the dart executable, the snapshot file, and a tiny shell script to invoke the app1. This is a huge relief coming from Ruby, which required a whole installation of executables and libraries in order to run a single app.

    Those three files are what we distribute today on our GitHub release page. But finding, downloading, and opening an archive and adding it to the command-line path is still a barrier to entry that we'd like to avoid where possible. Today we're taking a step in that direction by releasing a Dart Sass package on Chocolatey, the Windows package manager. You can install it now using:

    $ choco install sass -prerelease
    

    This will give you a sass executable that runs Dart Sass on the (really fast) Dart VM.

    A large percentage of Sass users are on Windows, and it hasn't always been easy for them to get the latest and greatest Sass versions without a bunch of installation headaches. I'm excited that we can start taking advantage of our new infrastructure to fix that.

    In addition to Chocolatey, we'd love to get Dart Sass on Homebrew for our OS X users. If you're interested in helping out with that, let us know—this issue would be a great place to start!


    1. There's also an open issue for bundling the VM and the snapshot into a single executable file, which would allow us to pare down our distribution to a single file.  ↩

  • Announcing Dart Sass

    Posted by Natalie Weizenbaum 7 months ago

    Over the past few months, I've been quietly working on a new project. Today I'm ready to announce Dart Sass to the world. It's a totally new implementation of Sass, designed to be fast, easy to install, and easy to hack on. It's not yet complete—I'm steadily working my way through sass-spec—so today I'm just releasing version 1.0.0-alpha.1. But it's solid enough for you to download, play with, and start filing issues.

    You can download a standalone archive from the release page—just extract it, add the folder to your path, and run dart-sass. Dart also compiles to JavaScript, so if you have npm installed you can install the JS version by running npm install -g dart-sass. And, if you happen to be a Dart user yourself, you can install it using pub global install sass.

    Why Rewrite Sass?

    Over the past few years, there have been two primary implementations of Sass. Ruby Sass was the original, written mostly by me with substantial help from Chris. It's high-level and easy to hack on, so it's where we iterate on new features and where they first get released. Then there's LibSass, the C++ implementation, originally created by Aaron and Hampton and now maintained by Marcel and Michael. It's low-level, which makes it very fast and easy to install and embed in other languages. In particular, its Node.js bindings are a very popular way to use Sass in the JavaScript world.

    Each implementation's strengths complement the other's weaknesses. Where LibSass is fast and portable, Ruby Sass is slow and difficult for non-Ruby-users to install. Where Ruby Sass is easy to iterate on, LibSass's low-level language makes it substantially harder to add new features. A complementary relationship can be healthy, but it can also mean that neither solution is as good as it needs to be. That's what we found when, in May, Marcel officially left the LibSass team1.

    Without two people's worth of effort, we were no longer sure that LibSass could keep pace with the speed Chris and I wanted to introduce changes into the language. And it had been clear for a long time that Ruby Sass was far too slow for use cases involving large stylesheets. We needed a new implementation, one that could generate CSS quickly and add new features quickly.

    Why Dart?

    We considered a number of possible languages, and ended up deciding on Dart for a number of reasons. First, it's really fast—the Dart VM is generally much faster than JavaScript VMs, and early benchmarks2 indicate that, for large stylesheets, Dart Sass is 5-10x faster than Ruby Sass and only about 1.5x slower than LibSass. I'll hazard a guess that it would be about 1.5-2x faster than an idiomatic JS implementation, but I can't say for sure. And Dart's performance continues to get better all the time.

    At the same time, Dart is easy to work with—much more so than C++, and to some extent even more than Ruby for such a large project. Granted, not as many people are familiar with it as with JavaScript, but language implementations don't tend to get many external contributions anyway. I'll be doing most of the work on the new implementation, and Dart is the language that I'm personally most comfortable with at the moment (when I'm not working on Sass, I'm on the Dart team). Using Dart gives me a lot of extra velocity.

    Unlike Ruby or JavaScript, Dart is statically typed, so every value's type can be figured out without running the code. Unlike C++, it's garbage collected, so we don't have to worry as much about cleaning up after ourselves. This makes it easy to write, easy to modify, and easy to maintain. Maybe even more importantly, it makes it easy to translate to other programming languages, which will help LibSass get new features faster.

    The last reason we chose Dart is something that only a few other languages can boast: JavaScript compatibility. Dart can be compiled to JavaScript, which can be used directly in Node.js or even potentially run in a browser. A huge chunk of the Sass ecosystem built on node-sass, and we intend to make the JS version of Dart Sass as close to API-compatible with node-sass as possible, so that it can easily drop into existing tools and build systems.

    The only downside is that there's a speed hit: Dart Sass is about twice as slow running on V8 as it is running on the Dart VM. However, this still puts it solidly 3-4x faster than Ruby Sass. Ultimately we also hope to provide an easy path for users of the JS-compiled version to move to the Dart VM version as little friction as possible.

    What Will Happen to The Other Implementations?

    Nothing's changing about LibSass's development. Michael's hard at work adding features from Sass 3.5, and we expect that process to continue as new language features are added. The only difference is that LibSass will no longer be required to be strictly compatible with the latest version of the language in order for that version to launch, since it will no longer be the only implementation with reasonable performance.

    More flexibility translates into faster LibSass releases that prioritize the features users want most. Strict compatibility meant that important features like CSS custom property support can't be released until all the tiny tricky edge cases that were in the corresponding Ruby Sass release, like :root unification, are implemented as well. We'll still strive for as much compatibility as possible, but we won't let that stand in the way of velocity.

    Ruby Sass, on the other hand, will eventually go away unless a new maintainer appears. We don't want to make the transition sudden and risk fracturing the ecosystem: Chris and I are committed to maintaining it for one year, which includes keeping the language in sync with any new additions in Dart Sass. If anyone is interested in volunteering as a maintainer after that period, we'd be thrilled to mentor them and teach them the codebase over the coming year. But if no one steps up, Ruby Sass will be officially considered deprecated and unmaintained.

    I want to emphasize that we aren't making the decision to stop developing Ruby Sass lightly. This is a big change, and it's not an easy one for me—I've worked on Ruby Sass continuously for almost ten years now, and it's difficult to let that history go. But Chris and I have discussed this thoroughly, and we're convinced this is the right move. We only have so much time to devote to Sass, and it no longer makes sense to put that time into an implementation that's so slow as to be infeasible for many of our largest users.

    What Next?

    Before we release the first stable version of Dart Sass, there are a few big things on our to-do list:

    • Full sass-spec compatibility. There are still a bunch of corners of the language where Dart Sass does the wrong thing, especially with respect to @extend. I don't expect any individual incompatibility to be especially difficult to address, and sass-spec is pretty comprehensive, so it's just a matter of steadily reducing the number of failing specs until it hits zero.

    • Close-enough node-sass render() compatibility in the npm package. The node-sass render() API is the main entrypoint to LibSass in the JavaScript world. It's how build systems run Sass, how users define custom Sass functions, and how Eyeglass passes modules to Sass. We want to support this API with enough fidelity that the existing ecosystem works with JS-compiled Dart Sass.

    • Dart Sass compatibility in Ruby Sass. There are some cases where Dart Sass intentionally differs from Ruby Sass, particularly when Ruby Sass's behavior is considered a bug. We should add deprecation messages in Ruby Sass and, if we can do so with minimal disruption, add support for the new behavior.

    There's plenty more we'd like to do eventually, like supporting Sass in the browser and providing a node-sass-compatible wrapper for Sass on the Dart VM, but those aren't blocking the initial release.

    Onward Into the Future

    The next couple months will see a lot of work go into getting Dart Sass stable and compatible, and getting Sass 3.5 features into LibSass. I think it's likely that early 2017 will see a stable release of Dart Sass and a 3.5 release of LibSass. At that point we'll set our sight on the big features and start working towards Sass 4.0 and its brand new module system.

    Dart Sass is a big change, but it's an exciting one as well. It'll allow us to get new features into users' hands faster, and to make those features run faster. It'll make it possible for users to trivially install and run the reference implementation. And it'll give us a performant way to run Sass in pure JavaScript Sass for the first time ever. The benefits are large and tangible, and I'm confident they're worth the costs.


    1. I say "officially" because he's still contributing to the project when he can, just not in an official maintainer capacity. ↩

    2. Caveats apply: I'm not a benchmarking expert, and these tests were ad hoc and run against non-representative source stylesheets. If anyone is interested in working on more scientific benchmarks, please let me know! ↩

  • Sass 3.5 Release Candidate

    Posted by Natalie Weizenbaum 9 months ago

    I've just pushed the button to release Sass 3.5.0-rc.1. If it seems like it's been a while since the last release, that's true! But there's a good reason. We decided to enter feature freeze after the 3.5 release to give libsass, the super-speedy C++ implementation of Sass, time to reach feature parity with Sass 3.4. Libsass is much younger than Sass, and C++ is generally a slower language to work in than Ruby, so this took some time. But it paid off: libsass is now almost 100% compatible with Ruby Sass, differing only in a few small bugs.

    After the feature freeze lifted, we were primarily focused on designing the new module system that will be the central feature of Sass 4.0. But we also found some time to add some new features, which are the focus of this release.

    CSS Custom Property Support

    Sass 3.5 now fully supports CSS custom properties. These posed a particular challenge for us, since the custom property syntax is extremely broad. You can put just about anything on the right-hand side. For example, this is totally valid, meaningful CSS:

    .wacky-property {
      --property: .%(#@$~`^[^_+]<;:"}"|?)*+
    }
    

    In particular, this means that SassScript expressions are also valid CSS, which poses a problem for our goal of CSS compatibility. Wherever possible, we want valid CSS to mean the same thing in Sass as it does in CSS. So treating custom properties just like normal properties—which we did in 3.4—wasn't a good solution. Not only was some valid CSS interpreted differently, some of it wasn't even possible. The following CSS, taken straight from the Polymer docs, was next to impossible to represent in Sass:

    :host {
      --my-toolbar-theme: {
        background-color: green;
        border-radius: 4px;
        border: 1px solid gray;
      };
    }
    

    On the other hand, we needed some way of including dynamic SassScript values in custom properties. So we decided on a compromise: we'd treat custom properties like we do selectors and at-rule values, and only allow #{} as a means of including Sass values. While technically this is plain CSS, it's a very small surface area and it's very easy to escape, so we're not too worried. This means that in 3.5 you can write:

    :host {
      --my-toolbar-theme: {
        background-color: #{$toolbar-background};
        border-radius: 4px;
        border: 1px solid gray;
      };
    }
    

    New Data Type: First-Class Functions

    In preparation for the module system that's coming in Sass 4.0, 3.5 adds a new data type: first-class functions. This is just a way of referring to a function that's more specific than just its name. You can get a first-class function by passing its name to get-function($name), and you can pass it to call() where you used to pass the function name.

    You might be wondering, "Why is this useful? I could already just pass the function name." Well, right now, Sass has global scope. All functions (as well as variables, mixins, and selectors) are visible to any code that's executing later on. This makes some things, like call(), simple, but it also causes a lot of problems. It's way too easy to accidentally overwrite a variable or function that was defined elsewhere, and it's way too hard to figure out where any given name was originally defined.

    We aren't quite ready to talk widely about our plans for the 4.0 module system, but one of the things we're sure of is that it won't use global scope. Each Sass file will only be able to see a limited number of the names that have been defined, and Sass libraries in particular won't be able to see anything defined by the end-user stylesheets that import them. First-class functions allow users to pass functions they define to libraries.

    Any stylesheets that are currently passing around function names as strings should switch to passing first-class functions instead. To this end, calling call() with a string has been deprecated. It won't actually break until 4.0, when it won't be much use anyway, but we strongly encourage users to switch to get-function() immediately.

    New Syntax: Bracketed Lists

    The new CSS Grid Layout module added a new type of syntax: identifiers surrounded by square brackets. We're always striving to be totally compatible with CSS, which meant we needed to support these brackets as well. Here's what they look like in CSS:

    .container {
      grid-template-columns: [first] 40px [line2] 50px [line3] auto [col4-start] 50px [five] 40px [end];
      grid-template-rows: [row1-start] 25% [row1-end] 100px [third-line] auto [last-line];
    }
    

    The solution was clear: Sass already has a list data type, so we'd just allow lists to have square brackets. So [first] is just a list containing the unquoted string first. Like all Sass lists, bracketed lists can either be space-separated or comma-separated: [foo bar baz] and [foo, bar, baz] are both lists containing three elements.

    We've also added function support for bracketed lists. The is-bracketed($list) function returns whether a list is bracketed or not, and join() has a new $bracketed parameter that allows the caller to choose whether or not the resulting list will have brackets (by default, the result is bracketed if the first list is).

    Smaller Features

    We've added a content-exists() function that returns whether or not a content block was passed to the current mixin. This allows mixins to optionally take a content block, rather than having to define one mixin that takes content and one that does not.

    We've added the ability to add a trailing comma to argument lists. This matches the behavior of lists and maps.

    We've added a $weight parameter to the invert() function. This is a percentage between 0% and 100% that indicates how inverted the resulting color should be. It defaults to 100%.

    The Road to Release

    This is just a release candidate, but it's in a place that we'd be happy shipping it as the final release. We're not doing so because, now that we've reached feature compatibility with libsass, we're committed to staying there.

    Unfortunately, since Marcel Greter moved on from the project, libsass has been moving pretty slowly lately. If you or anyone you know would be interested in working on a project that would benefit thousands of people, we're still looking for new contributors!

    Until we have libsass compatibility, 3.5 will stay at a release candidate level. But don't let that stop you from trying it out and letting us know what you think! We're always interested in hearing feedback on the mailing list!

  • Thank You, Marcel!

    Posted by chriseppstein about 1 year ago

    You may not know Marcel Greter, but you almost certainly know his work. For more than two years, he has been the #1 contributor on LibSass and is the #2 contributor for the history of the project. His monumental efforts, together with Michael Mifsud, are what has kept LibSass thriving recently and why it has reached feature parity with Ruby Sass and is now used by more users than Ruby Sass every day.

    Presently, as we are pivoting to a new development model and release cadence, Marcel has decided it is the best time for him to exit the LibSass core team. We are truly sad to see him go, but we wish him well in all his new endeavors. Please join us in thanking him for all his hard work. You can follow and tweet him @mgreter.


    Unfortunately, this leaves the LibSass project with some big shoes to fill. LibSass is a popular project that has more than 1.5 Million downloads a month and as of right now, it is in desparate need of new contributors.

    We have a bunch of new features planned for Sass 3.5 that need to be implemented in LibSass and we want to do a coordinated release of both Ruby Sass and LibSass as soon as we can. If you can write production quality C++ and want to build parsers and compilers, we would love to help you become a contributor. If you don't have free time, but still want to contribute, consider asking your workplace to support the project by donating a percentage of your time.

  • Dropping Support For Old Ruby Versions

    Posted by Natalie Weizenbaum over 1 year ago

    As of version 3.5, Ruby Sass will drop support for Ruby 1.8.7 and Ruby 1.9.3. We will continue to support Ruby 2.0.0 and higher.

    Ruby 1.8.7 was retired by the Ruby maintainers in June 2013, and Ruby 1.9.3 was retired in February 2015. Despite that, we continued to maintain support for older versions because Ruby 1.8.7 was installed by default on Mac OS X through Mountain Lion (which was released in July 2012).

    There are many users of Sass who aren't independently users of Ruby. We wanted to minimize the amount of work these users need to do to use Sass, which means letting it run on their machine without also requiring them to install a new language.

    That decision wasn't without costs, though. Most seriously, recent versions of the listen package didn't support older Ruby versions, and older versions of RubyGems weren't clever enough to avoid downloading them on incompatible Ruby versions. To work around this, we bundled an older version of listen with Sass and used it for users who didn't have a compatible version installed elsewhere, but this produced constant compatibility headaches.

    These headaches led us to reevaluate our policy for supporting older Ruby versions. We still cared a lot about users' built-in Ruby versions, but we couldn't support them forever. We needed a way to determine when the benefit of dropping support outweighed the costs.

    We decided to use the analytics data for sass-lang.com to approximate the proportion of our user base that was still using operating systems that shipped with old Ruby versions. Before we looked at the data, we decided that we would drop support for a Ruby version if it had been retired by the Ruby maintainers, and less than 2% of our visitors across the previous month were using an OS that shipped it by default.

    Once we did that, we looked at the data. 34.3% of our visitors were using OS X, and 1.4% of OS X users were using Mountain Lion or earlier. We were clearly able to drop support for 1.8.7. In addition, 1.9.3 was never shipped with OS X so we were able to drop it as well. Ruby 2.0.0, despite retired last week, was shipped with the most recent OS X version—we won't be dropping support for it any time soon.

    sass-lang.com visitors by operating system

    For Sass 3.4, we're just planning on printing deprecation messages for users of deprecated Ruby versions. But once 3.5 releases, support will be fully dropped and we'll switch to using listen as a proper gem dependency. If you're on an older version of OS X and you haven't upgraded your Ruby version, there are some simple instructions on the Ruby site for how to do so easily using Homebrew.

  • Cleaning Up Interpolation

    Posted by Natalie Weizenbaum over 1 year ago

    Interpolation—the ability to add variables and other snippets using #{...}—is one of the handiest all-purpose features of Sass. You can use it just about everywhere you might need to inject a variable, a function call, or some other expression. In most of those places it just plops the value into the surrounding text. It's straightforward, easy to understand, and useful, which is exactly what we want from a feature.

    Unfortunately, that's only true in most places. For complicated historical reasons, there's one place where interpolation goes a little bit bananas: inside an expression but outside quotes. Most of the time, it makes sense; if you write display: -#{$prefix}-box, you'll get what you expect. But if any operators like + are used next to the interpolation, you start to get weird output. For example, $name + #{$counter + 1} might return an unquoted string containing the text name + 3.

    This is really weird behavior. Why does + behave differently here than it does everywhere else? Why is it treated as plain text when $name gets evaluated normally? This behavior is confusing, inconsistent, and not particularly useful, which are very much not things we want in a feature. So why do they exist in the first place?

    Complicated Historical Reasons

    If you don't care for a history lesson, skip on down to A Brave New World.

    Way back in the dawn of time, when the indented syntax was the only syntax, Sass had a distinction between "static" and "dynamic" properties. A static property was basically plain CSS; it was declared using property: value, and the value was used as-is without any further processing. If you wanted to use a variable or a function, you had to use a dynamic property, which was declared using property= value. A You'd see a lot of stylesheets like this:

    .border
    
      border-width: 4px
    
      border-style: solid
    
      border-color= !background_color
    

    Also, in the dawn of time, variables used ! instead of and couldn't include hyphens. The dawn of time kind of sucked. But it was in this context that we first added interpolation. We wanted to allow properties like border with multiple values to be partially dynamic, so we decided to follow in Ruby's footsteps and allow #{} to be used to drop in values. Soon stylesheets started looking like this:

    .border
    
      border: 4px solid #{!background_color}
    

    That's so much better! And for a while, all was calm.

    Then Came SCSS

    It eventually became clear that users really strongly wanted their stylesheets to look like CSS, so we sat down and started work on the syntax that would become SCSS in the release that would become Sass 3. As part of this work, we decided to get rid of the distinction between static and dynamic properties altogether. Having all properties work the same way was obviously great for users, but it meant we had to figure out how to merge the two syntaxes with a minimum of pain.

    This was mostly straightforward, since the old expression syntax was pretty much universally invalid CSS or something that emitted its CSS value anyway. But interpolation proved tricky. Backwards compatibility is really important to us, so we wanted to be sure that all the places interpolation was used—or could theoretically be used—in Sass 2 would continue to work in Sass 3, even though everything around them was now fully parsed.

    Our solution was to make basically anything around #{} that wasn't obviously part of a plain-CSS expression turn into a string. That way, hopefully any weird corner cases that people had would keep working when they upgraded. This led to the weird behavior I described above, but at the time our top priority was making it as easy as possible for users to migrate to Sass 3. We decided the weirdness was worth it, and shipped it.

    A Brave New World

    Flash forward to today. We're now starting work on the next major release, Sass 4, and (I dearly hope) no one's written any Sass 2 stylesheets in years. A major release is a great opportunity to clean up this bit of historical cruft, and after discussing it extensively on the issue tracker we decided to make the change.

    There are three major steps in a backwards-incompatible change like this. The first is to design the new syntax, which was pretty easy here, since it's basically just "do what everyone thought it did already." We just had to take that general notion and suss out the specifics.

    We ended up framing it as #{} being, syntactically, part of an identifier. When you write -#{$prefix}-box, Sass parses it as a single identifier containing "-" followed by the value of $prefix followed by "-box". Even if you write #{$font} all on its own, it's parsed as an identifier that only contains the value of $font. This way, interpolation doesn't have weird behavior around operators any more than identifiers ever did.

    Once we had a design, the second step was to deprecate the old behavior. The meat of deprecation is figuring out when to print a warning, and that was pretty tough here. We didn't want to warn for situations that would continue to work, even when they involved operators—for example, 12px/#{$line-height} will print the right thing in the old and new worlds (although for slightly different reasons), but 12px+#{$line-height} won't.

    I won't go into the gory details of how we got deprecation working here; that's what the GitHub issue is for. Suffice it to say that it involved a lot of special cases, including some where a deprecation warning can be printed based on how a value is used rather than how it's written. I'm pretty happy with where it ended up, though; I suspect it'll catch 99% of cases that will actually break in practice.

    Another exciting bonus was the ability to automatically update code. This doesn't always work when introducing backwards-incompatibilities, but in this case we were able to make sass-convert convert deprecated uses of interpolation into Sass 4-compatible code. It has some false negatives—it only converts cases it can prove will be incompatible—but it's enough to get users a long way there.

    The final step once the deprecation was in place was to move to the master branch (which will eventually become Sass 4), rip out all the old behavior, and implement the new. And it was wonderful. Deleting gross code and replacing it with something clean feels like taking a shower after spending a day hiking through dust under a hot sun. And after working on this feature for weeks, I was happy to see the other end of it.

    Checking it Out

    Sass 3.4.20, released today, was the first release to include the deprecation warnings for the old syntax. If you want to check whether you've got any deprecated interpolations lurking in your stylesheets, just gem install sass and recompile your stylesheet. And if you do find some, try running sass-convert --recursive --in-place . to fix a bunch automatically.

    If you want to try out the new syntax, 4.0.0.alpha.1 was also released today. You can get it with gem install sass --prerelease. But beware: it is alpha software, so it may change in the future. We generally try to keep even our prereleases pretty stable, but there's also a chance you'll run into a bug.

    If you do find a bug, please file it on the issue tracker. Even if it's something as simple as a typo, we want to know. If we've deprecated something that should be valid, we especially want to know. And if you just have a question, feel free to tweet at @SassCSS or post it on the mailing list.

  • Sass 3.4 is Released

    Posted by Natalie Weizenbaum almost 3 years ago

    We've been trying to increase the pace of Sass releases, and it looks like we've succeeded. A mere five months after the release of Sass 3.3, we're announcing the release of Sass 3.4.0, codename Selective Steve. Faster releases mean fewer major features per release, so there are only two big new things to talk about (although there are plenty of little improvements you can read about in the changelog). As the version name suggests, both of these features have to do with selectors.

    Using & in SassScript

    "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. In Sass 3.4, we added support for something new: the parent selector, &.

    Most Sass users will probably recognize & from its previous appearances in selectors around the world, where it's used to explicitly refer to the parent selector. For example, in .parent { .child & { ... } }, & refers to .parent, and this compiles to .child .parent { ... }.

    Now & works much the same way in SassScript. It refers to the same parent selector, but instead of just being dropped in it's exposed as a list of lists to make it easy for functions to inspect and manipulate it. For example, if you write .foo .bar, .baz { $selector: & }, $selector will be ((".foo" ".bar"), (".baz",)).

    We had originally slated this feature for version 3.3, but we took it out when we realized it was really hard to use these selectors in a way that didn't break when they contained commas. Because of that, we decided to delay it for a version to give us time to come up with its compantion feature: selector functions.

    Selector Functions

    The problem with just exposing & was that the only way to use it with other selectors was by glomming them together as strings. This works okay in simple cases, but when you write #{$selector} .child and $selector is .foo, .bar, you want .foo .child, .bar .child but you get .foo, .bar .child. This is no good at all.

    To solve this, we added a slew of functions that use Sass's powerful built-in selector logic to do the right thing. For example, you can now write selector-nest(".foo, .bar", ".child") and get exactly what you want. These functions all return the same sort of nested-list representation that & uses,but they're very liberal in what they accept: anything from nested lists to plain old strings.

    If you want to see every selector function we thought up, check out the changelog. I do want to highlight a few that I'm particularly fond of, though. You've already seen selector-nest(), and selector-append() is a close relative. The difference between them is whitespace: selector-nest() adds a space between its selectors, where selector-append() doesn't. This means that selector-append(".foo, .bar", "-suffix") returns .foo-suffix, .bar-suffix.

    Another function I like a lot is selector-replace(). This does a search-and-replace of one selector within another, but it's a lot more clever than your basic string replace. It uses Sass's @extend logic to replace selectors semantically, as though every element matched by the replacement selector was also matched by the replaced selector. For example, selector-replace(".foo.bar.baz", ".foo.baz", ".qux") returns .bar.qux.

    The last really powerful function I want to draw your attention to is selector-unify(). This takes two selectors and returns a new selector that matches only elements that are matched by both input selectors. This is an operation Sass uses a lot internally, and now users can access it as well. For example, selector-unify(".foo.bar", ".bar.baz") will return .foo.bar.baz.

    What's Next?

    I won't rule out the possibility of Sass 3.5 existing, but Chris and I plan to focus pretty hard on Sass 4.0. The big feature for 4.0 is going to be @import, or rather the lack thereof. Our current import system is beginning to show its age in a major way, and we intend to replace it wholesale, up to and including the name. As of 4.0, the recommended way of pulling in other Sass files will be @use.

    Among the features we're planning for @use are two that have been widely requested. You'll be able to import CSS stylesheets directly into your Sass ones, and each stylesheet will only be run once, no matter how many times it's imported.

    Until then, though, run gem update sass and enjoy Selective Steve!

  • Sass 3.3 is Released

    Posted by Natalie Weizenbaum about 3 years ago

    After ironing out a bunch of bugs in numerous release candidates, we're finally ready to release Sass 3.3.0, codename Maptastic Maple, for public consumption. This release has a lot of exciting new features that you can read about in full in the changelog, but there are three that I want to draw your attention to in particular.

    Maps in SassScript

    As language designers, most of our job is to listen to feedback from users and act upon it. This is tricker than it sounds: users are very good at knowing the precise thing that they want to accomplish, but they tend not to have a sense of how that fits into the big picture. So we take a large volume of user requests, try to distill the core needs that aren't being met, and see if we can come up with features that hit as many of those as possible as simply as possible.

    SassScript maps are a great example of this. We had a lot of users requesting things like variable interpolation, so they could write things like $#{$theme-name}-background-color. Other users wanted built-in functions that worked with lists of pairs, or a way to get the name of a variable that was passed to a function. We eventually realized the underlying feature that people actually wanted: a way to associate values with names.

    Most programming languages have a notion of maps1, which are associations from "key" objects to "value" objects. Sass 3.3 adds support for these as a first-class data structure. The syntax is designed to be very similar to that used for @media queries. They look like this:

    $map: (key1: value1, key2: value2, key3: value3);
    

    Unlike lists, maps must always be surrounded by parentheses. (), which previously referred to an empty list, now also refers to an empty map; both list and map operations will work on it.

    Maps can't be used as CSS values, since they aren't valid CSS syntax. However, there are a number of new built-in functions that allow user-defined mixins and functions to use them. Here are a few particularly useful ones:

    • map-get($map, $key) looks up a value in a map using its key. For example, using the example above, map-get($map, key2) would return value2.

    • map-merge($map1, $map2) merges two maps together. The keys in $map2 overwrite those in $map1, so this is also a good way to add values to a map. For example, map-merge($map, (key1: new-value)) would return (key1: new-value, key2: value2, key3: value3).

    • map-remove($map, $key) removes a value in a map. For example, map-remove($map, $key) would return (key: value2, key3: value3).

    In addition to the new map functions, all the existing list functions also work on maps. The list functions will see each map as a list of pairs. For example, nth($map, 1) will return (key1 value1). Not only that, but @each has new syntax for working with both maps and lists of pairs. For example:

    @each $header, $size in (h1: 2em, h2: 1.5em, h3: 1.2em) {
      #{$header} {
        font-size: $size;
      }
    }
    

    produces:

    h1 {
      font-size: 2em;
    }
    
    h2 {
      font-size: 1.5em;
    }
    
    h3 {
      font-size: 1.2em;
    }
    

    Source Maps

    Continuing the map theme, Sass 3.3 comes with support for generating source maps when compiling to CSS. Source maps are a standard format for telling browsers how files they consume got generated. For Sass, this means that browsers' development tools can now tell you exactly which line of your Sass source file each style rule came from. Currently this is only well-supported in Chrome, but hopefully other browsers will add support soon.

    When compiling Sass from the command line, all you need to do to generate source maps is pass the --sourcemap flag. Sass will automatically generate a .css.map file next to the generated .css file. All you have to do then is make sure your .scss or .sass file is visible to the browser, and you'll be good to go.

    More Flexible &

    When we released Sass 3.02, we added support for SCSS, which meant we had to actually parse all the selectors in the document. This meant that you couldn't just plop the parent selector, &, anywhere in a selector. Overall this was an improvement: it caught more errors and encouraged users to write more flexible mixins.

    Unfortunately, it also made one important use-case harder. With the rise in popularity of BEM, OOCSS, and SMACSS, people became more and more interested in adding suffixes to classes. When using Sass, they wanted to write mixins to do this, and the restrictions on & made that very hard to do.

    In Sass 3.3, we're loosening these restrictions. You can now write &-suffix (or &_suffix, or even &suffix if you really want) and Sass will make it work. If this fails to apply—for example, if & is *—Sass will print a helpful error message.

    Deprecation: Variable Scope and !global

    We don't always get everything right the first time, and in order to make Sass the best language it can be we occasionally have to change old behavior. Sometimes this happens in ways that might make existing stylesheets stop functioning, so we have a policy of printing warnings for stylesheets that are going to change in the future.

    Sass 3.3 adds a number of deprecations, but the biggest one by far has to do with the way variable scope works. Up until now, when you wrote $var: value in a function, mixin, or CSS rule in Sass, it could do one of two things. If there was a global variable named $var, it would overwrite that variable. Otherwise, it would create a local variable that was only visible within the current set of curly braces.

    This was a pretty big problem, since any given variable assignment could potentially be modifying a variable that it had no way of knowing existed. We want to migrate to a better system where assigning to a variable in a local scope won't overwrite a global variable unless the assignment explicitly says to do so, as in $var: value !global.

    In order to avoid breaking existing stylesheets, we haven't made this change yet. Instead, if a global variable is overwritten by a local declaration, we now print a deprecation warning suggesting that the user add !global. Right now, !global doesn't do much other than make the warning go away, but in a future release it will work as I described above.

    That's All

    Actually, there's a lot more, but that's all I have room for in this post. If you want to see the full assortment of new features, check out the changelog. You can also play with the new features on SassMeister or on your own computer by running gem update sass. Enjoy!


    1. Some languages call them "hashes", "dictionaries", or "associative arrays". JavaScript calls them "objects" for weird historical reasons. ↩

    2. Please forgive the awful design of my old blog. ↩

Next

Copyright © 2014 nex3 . Theme iaawd by poying