Getting To Know Stylus
If you are part of the front-end scene, you might have heard of Stylus, the distant cousin from Sass that nobody knows very well. Like Sass, Stylus is a CSS preprocessor which is written in Node.js. According to its GitHub repository, it describes itself as:
[…] a revolutionary new language, providing an efficient, dynamic, and expressive way to generate CSS.
Okay, revolutionary might be a bit exaggerated. But everything else is true.
What, another one!?
Kind of. But Stylus isn’t brand new. It has been around since the beginning of 2011, but I see it as having quite a discrete community. By the way, did you know the latest Mozilla Developer Network redesign has been made with Stylus? David Walsh, who’s been involved in the project, also wrote about how to get started with Stylus.
So what are the advantages of Stylus over Sass? Well, it is built in Node.js which sounds like a plus side to me. And while it is perfectly fine to use Sass in a Node workflow thanks to Node-Sass wrapper for LibSass, it does not make LibSass written in Node for all that.
Also Stylus has an extremely permissive syntax, which can be a good or a bad thing depending on your project, your team and your tendency to stick to strict coding guidelines. I think a permissive syntax should be fine as long as you do not involve too much logic in the stylesheet, and lint the code before committing.
All in all, Stylus and Sass both support pretty much the same things; you can have a look at the full list of Stylus features but don’t expect anything ground breaking (although there are some neat features). Stylus also supports multiple syntaxes although the line is much more blurry than Sass: you can write your styles pretty much how you want (indented, CSS-style) and you can mix and match within the same stylesheet (the parser for this must have been fun to write).
So what do you think? Want to give it a try?
Getting started
As stated before, Stylus is written in Node.js so we can install it like any other npm package:
$ npm install stylus -g
From there, either you can plug it into your Node workflow using the JavaScript API, or you can use the command line executable to compile your stylesheets. For the sake of some simplicity, we will use the stylus
command line tool but feel free to tackle it from a Node script, Gulp or Grunt.
stylus ./stylesheets/ --out ./public/css
The previous command tells stylus
to compile all Stylus stylesheets (.styl
) from the stylesheets
folder and to generate them in public/css
folder. Of course, you can also watch the directory for changes:
stylus --watch ./stylesheets/ --out ./public/css
Writing Stylus styles
If you are just getting started and don’t want to feel overwhelmed with a new syntax, know that you can write plain CSS in a .styl
file. Since Stylus does support the standard CSS syntax, it is perfectly fine to start with CSS code only to enhance it slowly.
Basic syntax
Regarding the syntax itself, almost everything is optional. Braces: why bother? Semi-colons: come on! Colons: ditch ’em too. Parentheses: please. The following is perfectly valid Stylus code:
.foo
.bar
color tomato
background deepskyblue
Kind of disturbing at first but we could get used to it, especially when there are syntax highlighters available. As you can probably guess, the previous code compiles into:
.foo, .bar {
color: tomato;
background: deepskyblue;
}
Variables
The most used feature from CSS preprocessors has to be the ability to define variables. It’s not surprise that Stylus offers it as well. Although contrary to Sass, they are declared with an equal sign (=
) rather than a colon (:
). Also, the leading dollar sign ($
) is optional and can be safely omitted.
// Defining a `text-font-stack` variable
text-font-stack = 'Helvetica', 'Arial', sans-serif;
// Using it as part of the `font` property
body
font 125% / 1.5 text-font-stack
Now there is something that Stylus does which Sass or any other preprocessor does not: property value look-up. Let’s say you want to apply a negative left margin of half the width; in Sass you’d have to store the width in a variable, but not in Stylus:
.foo
width 400px
position absolute
left 50%
margin-left (@width / 2)
By using @width
, we tell Stylus to fetch the value of the width
property of the current block, treating it as a variable. Pretty neat! Another interesting use-case for this is to conditionally output a property depending on whether or not it has already been defined:
.foo
// ... other styles
z-index: 1 unless @z-index
In this case, z-index
will be set to 1 unless .foo
already has a value assigned for the z-index
property. Couple this with mixins and you really have something.
Mixins
Speaking of which, let’s define a mixin since it’s probably one of the most popular features of Sass! A mixin in Stylus needs no specific keyword; it is a mixin as long as it has the parentheses (empty or not) at the end of its name.
size(width, height = width)
width width
height height
Along the same lines, including a mixin needs no specific syntax like @include
or +
:
.foo
size(100px)
You can even drop the parentheses if you feel so, in which case it looks like you use a completely standard (yet not) CSS property. This mechanism is referred to as transparent mixins as their inclusions are invisible.
.foo
size 100px
That might look an unnecessary trick at first glance, but this feature actually allows authors to extend the default CSS syntax if you think about it. Consider the following overflow
mixin:
overflow(value)
if value == ellipsis
white-space nowrap
overflow hidden
text-overflow ellipsis
else
overflow: value
If the given value is ellipsis
, it prints the well-known declaration triplet needed to have a one-line ellipsis overflow. Else, it prints the given value. Here is how you’d use it:
.foo
overflow ellipsis
And it will yield:
.foo {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
You gotta admit, that’s a pretty cool trick. While it might be confusing (and possibly dangerous), being able to extend the standard CSS properties with extra values is actually an interesting concept.
If you want to pass some content to a mixin, in a @content
fashion, it is possible through a {block}
variable. During the inclusion, you only have to prefix the mixin name with +
to pass it extra content.
has-js()
html.js &
{block}
.foo
+has-js()
color red
This code will compile to:
html.js .foo {
color: #f00;
}
Last very interesting feature of Stylus mixins: they always have a arguments
local variable containing all arguments (if any) passed to the mixin when included. This variable can be manipulated and treated as an array, for instance to fetch values at specific indexes using [..]
like in JavaScript.
Final thoughts
Going through all features and syntax tricks from Stylus would be too long and I think we already had a decent introduction, enough to get started at least!
As you can see, Stylus is extremely permissive. Of all existing tools to help writing CSS, Stylus is definitely the one bringing CSS the closest to a real programming language.
Note that Stylus also has its own framework in the same way that Sass has Compass, and it’s called Nib. Nib is a toolbox providing extra helpers and cross-browsers support mixins for Stylus.
Some people might like it, some people might not. My advice would be to be very rigorous with the syntax though. Dealing with such a tolerant syntax might not always be such a bliss. In any case, it’s nice to see some decent concurrence to Sass.