Lightning Fast Sass by Chris Eppstein
[APPLAUSE] Hi, everyone.
How are you doing today? [MURMURING] That's it? Come on.
[APPLAUSE] All right.
So it has actually been a lifelong dream of mine to visit Amsterdam ever since college.
I can't think of why.
And so I'm really excited to be here today to speak to you and to get the chance to visit this beautiful city.
One second here.
Ah, come on.
So Bruce stole some of my thunder.
As he said, I work on the Sass Core Team.
Been doing that for about seven, eight years now.
I have a number of Open Source projects mostly related to Sass.
And LinkedIn, a few years ago, hired me to do open source development, because they're big Sass users, and I wanted to be able to devote more of my time to that, and so they hired me to help keep it up-to-date.
So I don't know if you guys remember hearing it back when DHH said, "Bro, Ruby is really slow."
I think he said that.
I don't know.
But Matz was like, "Yes, it is very slow."
And for instance, a lot of Sass will be like, yeah, my style sheets take like, a second, two seconds, to compile.
Can we just talk for a second about how terrible that is? So this machine right here that you're looking at is a 3 gigahertz machine, which means in one second, it has 3 billion clock cycles.
And what we're doing is taking a file that looks a lot like CSS, and we're turning it into CSS.
We're not doing a lot of optimizations.
We're not doing anything as complicated as what LLVM is doing in a matter of milliseconds.
We've done something wrong.
And so, you might ask, who builds a compiler in Ruby anyway? Seems like a terrible choice.
But I like to think that the reason we picked Ruby was really because we were Ruby-ists.
And when we were working on our nice, gorgeous, very slow Ruby on Rails applications, we often would look and see some beautiful abstractions in our Ruby code, and then we'd flip on over to our CSS and go, uh, OK.
Pound it out.
And all my life, I've always thought that CSS could use a little help.
In fact, when I would interview candidates at my job, I would ask them, one of the questions was like, so if you could change something about CSS, what would you change? And if they couldn't come to and be like, oh, you know, maybe I'd had variables, or this, or that, or wouldn't it be cool if you could manipulate colors, I'd be like, yeah, OK.
But if they couldn't do, I'd be like, you should think about your web technologies.
The web is something that we as a community can affect.
And if you're just thinking about the web as a static thing that you have to use, then I don't think you're thinking about the web right.
So as Ruby-ists, we started working on, how can we make our Sass as beautiful as the rest of our Ruby applications? And so, I found this tool called Sass one day and thought, hey, this is pretty cool.
Maybe I'll help contribute to that.
And despite Ruby and its numerous issues I'm going t talk about, Sass has become quite popular.
In fact, worldwide people seem to have really adopted it.
And I consider that a testament to how much you guys really like Sass, that you're willing to install a language you're not using for any other reason, and learn the technologies there.
But it has been an impediment to our adoption.
And back when Sass came around, there really wasn't a concept of a CSS processor.
And now that it is, we have more competition.
So we have to stay on our toes.
And so some reasons why you wouldn't want to use Ruby for something like Sass is that it's slow, and they've done a lot of work on performance there, but it continues to be objectively slower than just about anything else.
So the other problem is that front-end devs really don't know it.
And even though there are teams that-- sorry about that.
So it actually kills me, though, that Ruby is actually losing to Node.js.
[LAUGHING] (man) Aw.
That wasn't nice.
But it's true.
You can see on this graph-- this is actually queries on Google of Node.js
versus Ruby on Rails.
And Node comes along and just completely takes the wind out of Ruby's sails.
So it kills me, but you know, you can't fight the trends.
You got to go with the flow.
And so a couple of years ago, Hampton Catlin got started on a project called libSass.
And once it was clear that they were really committed to making that project succeed, we had a serious conversation about what to do about it, because as it got closer and closer to feature parity, it started really becoming a problem for our users, because they wanted to use it.
It was so much faster, but also didn't have all the features, and they didn't quite know what to do.
You guys can tweet to my screen.
Let's be careful with that.
And there is a big drop-off after those first two.
Java kicking but still, and we want everybody, every programmer of every language, to be able to access the awesome power that is Sass.
And so we picked libSass because basically every language can take in a native binary and use it.
And so, we now have bindings to just about every language under the sun.
But because of those incompatibilities I talked about, we decided to make a big bet on libSass.
And so we paused Sass' feature development for basically the last year, and told libSass to hurry up, but that we wait for them to catch up in order to close that gap between the two languages, or the two implementations.
And we put out a call for developers to help contribute, and the community really came together.
libSass is now at about 98% compatibility, according to our tests.
There's no major feature that they're missing.
Mostly, there's small incompatibilities around corner cases, and that URL at the bottom, you'll be able to access later, can take you directly to the list of all the issues they're still working on, and they're rapidly trying to finish those up in the next month or so.
So was it worth it? So you guys might have heard of a project called Post-CSS? Raise your hand if you've heard of it.
So Post-CSS is another kind of CSS processor.
It tends to work a lot more with basic CSS syntax.
And they've said on their website that they're twice as fast as libSass.
That's no longer true.
We've done some optimizations, and we're now 10% faster.
But the important thing about this particular benchmark is they're really just measuring parse time and generation time.
So the actual doing the work part isn't part of their benchmark.
Because they can't control what their plug-ins do, so it doesn't really make sense to spec it.
But if you were to bring in an equivalent set of functionality that libSass implements, you will find that libSass is actually dramatically faster.
But libSass comes with a number of downsides.
Because it's a binary format, we have OS and hardware dependencies, which means we need to either distribute binaries, or compile on the system.
It's not a language that very many web developers know.
In fact, if you are a web developer who knows C++, raise your hand.
Oh, my God .
All of you are awesome.
And you need to help us.
So come talk to me afterwards.
There's like, at least a couple dozen of you.
Huge opportunity to contribute here.
But C++ is going to be slower to develop new features.
And additionally, even though we've closed the gap in terms of the language differences between libSass and Ruby Sass, we still have an ecosystem fragmentation problem, because every language has its own software download and distribution system, and so sharing Sass files continues to be a pain point to this day.
And to that point, I want to mention that to us, Sass isn't just a tool that you run a file through, and out comes CSS.
Sass is really designed from the beginning to be a tool that the community of web developers can use to share their best practices, their code, and their tools for writing CSS.
And so, we want people like yourselves to be able to take the cool ideas you have and share them with the world as code, not just a blog post with snippets you can copy and paste.
And since Sass is clearly one of the best front-end tools, we have to have Sass there.
We wouldn't want people to have to use tools with less features than Sass has.
That's a very big web.
And node-sass brings libSass to us.
And so, it's very easy to install, npm install node-sass.
If you are on a system that it can, it will download a binary and just install it for you.
But because Sass is more than just a tool, it's a community, personally that was the thing that I really got excited about Sass, and that was why I wanted to work on it.
And what I found was-- I made Compass to help bring that community together.
And it was the first time that you could use Sass outside of a Ruby on Rails application, and it made it possible to distribute Sass files using RubyGems.
And the reason for that is actually quite clear.
Compass, in my mind, is eight years old, seven years old, and many of its features have basically been superseded by new tools.
For instance, Autoprefixer is really great.
It's clearly a better API for working with browser compatibility.
So big fan.
You guys should use that tool for that particular job.
So for about a year now, I actually haven't been actively developing on Compass.
There haven't been any new features.
Instead, I've been working on making sure that libSass and my new Open Source project called Eyeglass is available.
So, we used to be distributing our Sass files as RubyGems.
The new approach that I'm asking everybody to switch over to is to distribute them as NPM modules.
If you don't use NPM in your application, can you raise your hand? Literally everyone.
There's like, a couple people over there.
We're not judging.
[LAUGHING] So Eyeglass.
Eyeglass is my new Open Source project.
It's something that LinkedIn has been heavily investing in.
We have several other engineers at LinkedIn who've been collaborating with me on it.
So first and foremost, you can distribute your Sass modules via NPM.
And once you've installed them, you just import them into your Sass files.
You're probably aware that NPM's dependency resolution system is a sandbox approach, which means every dependency that you have has its own dependencies.
And even if they have a common dependency, they can be at different versions.
Unfortunately, it doesn't work well for Sass.
Sass has a very global environment.
And so Eyeglass helps you sort that out by looking at the entire set of dependencies in your application, doing semantic versioning comparisons of the versions that you're using, and picking the one that will work.
And if it can't, then it will give you an error that you can fix, or at least yell at someone.
A few other things that Eyeglass will do for you is help you deliver assets from your modules and get them into the application with the right URLs, which turns out to be kind of tricky.
It allows you to import an entire directory of Sass files by importing an index.scss file.
So that's a nice thing, I think.
Instead of having at the directory, and then a file next to that directory with the same name, instead, you can go into the directory, make an index of that CSS file.
And now, if you import that directory, it will import that index file.
It's a node thing.
I think people like that.
It also provides a file system API, and we have a website called eyeglass.rocks.
It's kind of a placeholder site for now.
We're going to be doing more development over the next month or two.
So already on NPM, there are about 30 Eyeglass modules that you can download and use right away.
I would like this number to be quite bigger.
Maybe 100 to 200, so that's one of the reasons I'm here today, is trying to get people to convert their projects over.
So as I said, it's quite easy to use an Eyeglass module.
You just npm install, dash, dash, save-dev, and provide the module name, and then you can import that.
In Compass, you actually had to do a second step in between, which was go into your Config file and add a require the module.
And that sort of thing is no longer required.
Because of how node modules works, we can just walk through that directory and discover for you what node modules exist.
So this is my way of trying to describe the way that things flow through a typical application that is using Eyeglass.
So you will have an asset pipeline of some sort.
Eyeglass is not an asset pipeline.
Use Broccoli, use Grunt, use Gulp, or use Ember CLI.
Whatever you're using as your asst pipeline, eyeglass is designed to work with that, because really, all we do is take in whatever node-sass options you want to provide, decorate them some, munge them together, and then give those back to you that you can pass to node-sass.
Node-sass will then ask Eyeglass to import files for it if it can't find it itself.
And at the end of the day, it generates a CSS file into the location of your choosing.
So this is the config portion of the talk.
Sorry, this is not terribly interesting.
There are some things you'll need to do in your package.json
to make an Eyeglass module.
Basically, you have to add a keyword called eyeglass module, and you have to add a section called eyeglass to your package.json that
provides a little bit more information.
All this is documented on the website, so I'm not going to go into it too much here.
But those are the two steps.
The other thing you have to make is an Eyeglass exports file.
This file basically registers the module with Eyeglass.
It also lets you tell us where your Sass files are located.
And there are some other things you can pass in there as well.
The way this works is actually quite simple.
You provide an object as the function's option, and you say, here's the signature of the function, as it would be specified in a Sass file.
It will be past those arguments.
But those arguments are not Sass values.
Obviously, they have to come from somewhere.
So what happens is Sass goes to libSass.
libSass gives you these objects.
From there, you can simply do your work as you would, and then return back one of those Sass objects to node-sass by calling the done callback.
So this function is doing something very interesting.
It's returning the current time.
So basically, you pass the number, and you have to specify the units.
But after working with these functions for a while and the data types that they were specifying, I found them really cumbersome to use.
Coming from the Ruby environment, we had these really rich objects that took care of a lot of the underlying complexity that Sass values have baked into them, a lot of the assumptions and conventions that you would expect if you were working with those values in a Sass file.
A few of the things that it provides that I think are interesting are basically like, some casting functions.
For instance, numbers would lose their unit, so we can't do that.
So there's a class called Sass dimension, which is a number with a unit.
It does things like it lets you do math with other Sass dimensions, and will do automatic conversions for you.
Things like that.
It does a bunch of other things, but this is kind of the highlights.
So as you know, you've got a Sass file.
That's all well and good.
But you might have some images in your project, some fonts, things like that.
And if you're the person who's building a reusable component, and you need to reference those assets, it actually gets quite complicated, because you don't know anything about the application environment.
You don't know how they're serving.
You don't know if they're using a CDN.
You don't know if you can count on that file being relative to the file that you have even.
Maybe they're going to hash all their files, so that they're CD unfriendly.
So you as a module developer, you really can't make any assumptions.
The best you can do as a module developer than is to say, here are my assets.
I'm going to refer to them, and then as the application, you can configure the way your assets are going to be treated, and so as an application code, it's quite simple to use the assets.
You basically import them from the module, or even from your own application.
Add Import Assets, basically, is give me all my own apps, applicat-- my own apps assets.
And you can bring in the assets from various modules.
And then, it's quite simple to refer to these.
Basically, you just prefix the path with the name of the module, and then reference the location that they had said it was at.
Under the covers, what Eyeglass is doing is taking that asset, it's going to ask the application, how do you want this installed into your app? How do you want it to be named? And then it will return a URL to the Sass file that really suits the application's needs best.
It also lets you enumerate over all the assets.
You can just say like, asset list, and it will let you inspect all the assets that are provided.
Good for debugging, or iterating over things, et cetera.
Another low level feature that Eyeglass is providing as a file system API.
So this is actually something that in the Core Sass API, we have long fought against.
But as an extension, I think it makes sense.
And so, basically, we let you import, we let you get access to the file system.
You can check, do files exist? You can read in a directory of files.
Enumerate over those things.
Mess with the names, et cetera.
So here, you can see that we're generating a class based on the base name of a file, and just kind of doing our some of our own asset management that way.
So you don't have to use the assets module if you don't want to.
File system API is even a lower level thing.
But one of the annoying things about node-sass that we didn't have in Ruby Sass is that we don't know what file you're in when you call a function.
That would be really useful to know, but we don't.
What we do know is what file you're in when you do an import statement.
And so, to work around that, you have to do these import statements here.
Import, FS, and then some identifier.
And all that does-- well, root is special.
It's going to be the root of your application.
But any other identifier that you pass in, it's just the directory of whatever Sass file you're in.
And so then you can refer to that special identifier when you're constructing an absolute URL, and you'll be able to access that file directly.
And in this way, you can write Sass files that are portable across Windows or Unix-like systems.
So I get that Eyeglass is kind of nuts and bolts.
It's tools that need to exist.
It's a necessary component of the Sass ecosystem, but it's not the most exciting thing in the world.
I get it.
But someone's got to do it, and I'm happy to make sure it exists.
But the thing that I really get excited, and the reason that I've worked so hard on this, is to make sure that awesome people like yourselves can seamlessly and effortlessly share your code with each other.
And that's what really motivates me at the end of the day.
It's by David Piano, I think.
It's a really cool project, and if you do hardcore Sass development, something we're checking out.
But one of the cool modules that I would like to demonstrate for you here was made by an intern that we hired at LinkedIn this summer.
And it's a port of the Compass writing module to Eyeglass.
Do you guys know the Compass writing module anyone? A few hands.
So basically what it does is lets you generate a sprite map from your files.
You basically say, here's some files, put them into a single image, and then there's some basic functions you can call, and you don't have to know where those images are within the entire map.
It'll set the offsets for you.
It'll set the image dimensions for you.
And it just takes a lot of the grunt work out of using spriting.
So this is the API example that you can do.
So basically, you declare a sprite map with a function called sprite map.
You name it, anything you want.
And you tell it how you want the sprites laid out.
There's several possibilities-- horizontal, vertical, diagonal, and-- I forget, there's a fancy packing one that will like, optimally lay everything out into the smallest possible space.
And then you tell it which images.
And unlike Compass, that lets you do all kinds of different image files from even different modules, it's pretty cool.
So then, you can just generate some CSS using the sprite information, and what you get is a bunch of stuff that you probably didn't want to write by hand.
So that takes care of all of those yucky offsets, and using the power of extend, you can get really tight output.
So this is an example of the directory of images.
And then when you're done, it outputs a single file that is all of your sprites put together.
So another limitation from Compass was that they all had to be PNG files.
We've been able to really get away from that.
You can now use several file formats, and the files can be in different directories, which was something Compass didn't allow.
And also, Compass had this magical import.
You would say like, @import startup PNG, and it would bring in all of these special mixins for you.
And what we found was actually a lot of users were very confused by that approach.
And so we've taken away some of the magic, and made it just a little bit more obvious what's been going on.
So some takeaways that I wanted you to have after hearing this talk.
I want you to use libSass.
If you're using node in your build system already, and you're using Sass, there's really no reason to not switch over.
If you're using Compass, you're going to have a little bit of pain.
But it's for the best.
If you're using Eyeglass-- or if you need to distribute Sass files, please use Eyeglass to do so.
If you want to build on top of what other people have used, use Eyeglass to bring that stuff in.
We're not going to be building new features for Compass anymore.
If there's any horrible bugs, we'll fix them.
It will save you a lot of heartache and mistakes.
And lastly, what I want to tell you is that the Ruby Sass implementation is not dead.
We are actively maintaining it.
Ruby on Rails is still a huge customer of Sass that we want to support.
Neither Natalie nor I know C++ all that well.
And so, if we want to build a new feature for Sass, building it in Ruby first and getting it kind of fleshed out there is a good way for us to still do that, and so we're going to be maintaining Ruby Sass for quite some time going forward.
And that is what I have for you guys today.
I'm a little early, so I'm happy to take questions from Bruce now.
[APPLAUSE] Thank you, sir.
Please accompany me to my parlor.
Let's do it.
And sit in a comfy chair.
There's quite a lot of questions on Twitter.
And I have no idea whether people are trolling me or these make sense, because I don't use Sass, I'm afraid.
We should just give him the talk from over here.
So a few people mentioned Sass, Eyeglass and Post-CSS.
And somebody said, waiting for the inevitable comparison between the two.
So can we have a comparison between the two, please.
Well, so I did compare the speed.
I don't think anyone should be surprised.
If it is, that means we've done something wrong in C++.
But there is some plugins to Post-CSS, which are aiming to bring some of the features of Sass Post-CSS.
And I think those efforts are fine, obviously, if that's what you want to use.
But I think Sass, we've done a lot of thinking about our syntax.
We're very careful and judicious with it.
And it seems to have clicked with people.
That's why it's become popular.
And ultimately, I think Sass is designed to be shared, whereas a random file that's written to work with Post-CSS is going to require you to install lots of dependencies, whereas most vanilla Sass files, you just, all the features that you get with Sass is kind of batteries included.
So I think that's a good benefit.
But time will tell.
People vote with their shoes.
So, you know.
Thank you. this is a
question from Flurin Egger.
And you kind of answered it, and I tweeted back and said, did that answer at all, or do you want me to wrestle Chris some more? And he wanted wrestling.
So I'm prepared to wrestle.
I'd love to know if we can just custom functions without all the other magic of Eyeglass which I might not need.
There was no Eyeglass magic in there.
You could have passed that same value directly to node-sass, and it would work.
I think if you're doing that, you should still use node-sass-utils.
It's going to save a lot of the heartache.
You don't have to use Eyeglass to use node-sass-utils.
And there's a good chance that we'll end up maybe bringing the node-sass-utils into node-sass proper, once we've really worked out the API, and see how it performs, things like that.
So you don't have to use it.
There's some data copying that goes on, and then there's a little performance hit there.
So if you really are mission-critical, doing some stuff that needs to be quite fast and in tight loops and stuff, then that's another option you can consider.
Yeah, I should be afraid.
One of the questions-- there were several questions similarly asking similar things.
Any incremental compilation features in libSass? Incremental compilation.
I don't know what that means.
I'm not quite sure what that means.
If you asked that question, come find me afterwards.
Maybe we can talk about it.
But it basically just takes the Sass file and builds them.
Maybe you mean one file at a time, or something like that? Sorry.
It's entirely fair.
I don't know what it meant, you don't know what it meant.
I feel inadequate all of a sudden.
And so, it actually may be faster to just write something that's a little bit more complicated in Sass itself.
So that's one option.
What was the question? Which patterns or methods should you either use or avoid in order to get faster Sass compilation.
One thing I've always wanted to build for Sass is like, a profile for your Sass code.
But I haven't.
So maybe someone else will beat me to it.
A challenge there for Frontiers audience Sass profiler.
And last question.
this is one from me.
You have purple hair, the second best hair on the stage.
What's wrong with green? What's wrong with what? What's wrong with green? What's wrong with green? I don't like vegetables.
You don't like vegetables.
On that note, put your hands together for Chris Eppstein, ladies and gentlemen.