CSS preprocessors compared: Less vs. Sass vs. Stylus

Image for CSS preprocessors compared: Less vs. Sass vs. Stylus

(Warning: biased)

As the CSS preprocessor battle rages on between the three top players—Less, Sass and Stylus—deciding which one to use normally comes down to personal preference or project requirements. However, I think there are some major advantages to using Stylus that make development easier, so if you are already a hardened Less or Sass veteran, maybe this will change your mind. If you still don’t use a preprocessor…. c’mon. And if you are already in love with Stylus, good for you—you are awesome.

Syntax

The first, and perhaps most controversial, benefit to using Stylus is the syntax. Stylus provides more robust syntax options than any of the other preprocessors. If you are more comfortable with a straight CSS syntax (like Less and .scss) then you are in luck:  “Stylus transparently supports a regular CSS-style syntax.”


.syntax-css {
  background: red;
}

However, if you are used to the “real” Sass syntax (.sass), this is also valid Stylus.


.syntax-sass
  background: red

Now we get to the real benefit of Stylus’s syntax options: Stylus makes it possible to write CSS in a more readable method using fewer keystrokes. It’s completely stripped down to only the things that are necessary.

No curly braces. No semicolons. No colons. It’s the feng shui of preprocessors, really.


.syntax-stylus
  background red

This same minimalistic approach can be seen in all aspects of Stylus. For example, conditionals. In Stylus, conditionals are simple if / else if / else checks.

Stylus


mixin(a)
  if lightness(a) >= 50%
    background-color black
  else if lightness(a) < 50%
    background-color white
    color a
.conditional-example-1
  mixin(#ddd)
.conditional-example-2
  mixin(#555)

CSS:


.conditional-example-1 {
  background-color: #000;
  color: #ddd;
}
.conditional-example-2 {
  background-color: #fff;
  color: #555;
}

Sass follows the same style of conditionals, except the syntax is a little more complicated. You need to remember the following:

  • = to denote a mixin (@mixin if using .scss syntax)
  • Parameters need to be preceded by $
  • @ to denote a conditional

Sass:


=mixin($a)
  @if lightness($a) >= 50%
    background-color: black
  @else if lightness($a) < 50%
    background-color: white
    color: $a;
.conditional-example-1
  +mixin(#ddd)
.conditional-example-2
  +mixin(#555)

CSS:


.conditional-example-1 {
  background-color: black;
  color: #ddd;
}
.conditional-example-2 {
  background-color: white;
  color: #555;
}

This is by no means complicated; it’s just unnecessary.

Less takes a completely different approach. They have implemented what they refer to as “Guards.” This allows you to accomplish the same type of logic—it’s just more verbose, and they recommend repeating your mixin for every condition (or guard if you will).

Less:


.mixin (@a) when (lightness(@a) >= 50%) {
  background-color: black;
}
.mixin (@a) when (lightness(@a) < 50%) {
  background-color: white;
}
.mixin (@a) {
  color: @a;
}
.conditional-example-1 {
  .mixin(#ddd)
}
.conditional-example-2 {
  .mixin(#555)
}

CSS:


.conditional-example-1 {
  background-color: black;
  color: #dddddd;
}
.conditional-example-2 {
  background-color: white;
  color: #555555;
}

You can see that if for some reason you had a complicated mixin it could get interesting to continue to repeat your mixin in this manner.

Stylus simplifies this process by allowing you to just write conditionals the way you think about conditionals. No extra syntax to remember, no mixin duplication, just plain text that does exactly what you ask of it.

Placeholders

Placeholders are a list of properties and values that can be extended, but are not outputted into your CSS, and they’re another great feature of Stylus. They keep your CSS free of unused selectors when you only want to reuse a block of code.

In Stylus, you do this by prepending the selector with $.

Stylus:


$placeholder-1
  color red
.include-placeholder-1
  @extend $placeholder-1

CSS:


.include-placeholder-1 {
  color: #f00;
}

Sass has the same functionality, except you prepend the selector with %.

Sass:


%placeholder-1
  color: red
.include-placeholder-1
  @extend %placeholder-1

CSS:


.include-placeholder-1 {
  color: red;
}

Less has no such functionality. The closest you can get is mixins, yet you lose the ability to add selectors to the same block of code, resulting in repeated properties and a larger output.

Extend

Since the real value of placeholders is extending them, this brings us to our next feature: extend. All three of these preprocessors have “extend,” except there are some key differences in how they function. Stylus allows you to extend a class (or placeholder) and any nested selectors. It also allows you to do “Optional Extending” which is completely unique to Stylus.

Stylus – Extending Placeholders:


$extended-placeholder
  color red
.placeholder-extend-parent-1
  @extend $extended-placeholder
  background blue
.placeholder-extend-parent-2
  @extend $extended-placeholder
  background green

CSS:


.placeholder-extend-parent-1,
.placeholder-extend-parent-2 {
  color: #f00;
}
.placeholder-extend-parent-1 {
  background: #00f;
}
.placeholder-extend-parent-2 {
  background: #008000;
}

Stylus – Extending Nested Classes:


.extended-class
  color white
.extended-child
  background black
.class-extend
  @extend .extended-class, .extended-class .extended-child

CSS:


.extended-class,
.class-extend {
  color: #fff;
}
.extended-class .extended-child,
.class-extend .extended-child,
.class-extend {
  background: #000;
}

Stylus – Optional Extend:


$specialDesign
  color: #FFF
.optional-extend
  @extend .design !optional, $specialDesign !optional

CSS:


.optional-extend {
  color: #fff;
}

In LESS you can also extend nested selectors; however, placeholders and optional extending aren’t included features.

LESS:


.extend-parent-1 {
  &:extend(.extended-class);
  background: blue;
}
.extend-parent-2 {
  &:extend(.extended-class, .extended-class .extended-child);
  background: green;
}
.extended-class {
  color: red;
  .extended-child {
    background: black;
  }
}

CSS:


.extend-parent-1 {
  background: blue;
}
.extend-parent-2 {
  background: green;
}
.extended-class,
.extend-parent-1,
.extend-parent-2 {
  color: red;
}
.extended-class .extended-child,
.extend-parent-2 {
  background: black;
}

Sass allows you to do extends, but they are the least powerful of the three since you cannot extend any nested selectors or do optional extending.

Sass:


.extend-parent-1
  @extend %extended-class
  background: blue
.extend-parent-2
  @extend %extended-class
  background: green
%extended-class
  color: red

CSS:


.extend-parent-1 {
  background: blue;
}
.extend-parent-2 {
  background: green;
}
.extend-parent-1, .extend-parent-2 {
  color: red;
}

As you can see, Stylus allows you to do everything Less and Sass do with extend, as well as enjoy some key added benefits that are unique to Stylus.

Property Lookup

Another Stylus-only feature is Property Lookup, which allows you to look up the values of other properties and use them in valuable ways. For example, take one of the classic methods for vertically and horizontally centering an element.

Stylus:


.property-lookup-center
  position absolute
  top 50%
  left 50%
  width 150px
  height 80px
  margin-left -(@width / 2)
  margin-top -(@height / 2)

CSS:


.property-lookup-center {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 150px;
  height: 80px;
  margin-left: -75px;
  margin-top: -40px;
}

We have looked up the values of our width and height and used them to define margin offset. This gives us the benefit of not needing to redefine these values, and if they ever change, they only need to be updated in one location.

Property Lookup also allows us to define values that haven’t already been defined using the unless method.

Stylus:


position()
  position arguments
  z-index 1 unless @z-index
.property-lookup-unless-1
  z-index 20
  position absolute
.property-lookup-unless-2
  position absolute

CSS:


.property-lookup-unless-1 {
  z-index: 20;
  position: absolute;
}
.property-lookup-unless-2 {
  position: absolute;
  z-index: 1;
}

This allows us to eliminate duplicated properties if we ever need to overwrite anything in a mixin for specific situations.

Plugins & Libraries

On top of all of the impressive features Stylus has, some of its greatest benefits come from third-party plugins/libraries. A few of the best resources I have seen are:

  • Nib
  • Rupture
  • Jeet

Nib

Nib is the Compass of Stylus. It provides some exceptional mixins for cross-browser compatibility, including vendor prefixes. You can just write your CSS to match the spec and let Nib worry about the rest.

Rupture

Rupture is a slick utility for media queries with a refreshingly simple syntax.

Stylus:


+above(48em)
  .foo
    background black

CSS:


@media only screen and (min-width: 48em) {
  .foo {
    background: #000;
  }
}

No more needing to remember that long media query definition (or having to type it for that matter).

Jeet

Jeet is an amazing tool for grid systems, or as they call it, “A grid system for humans.” It provides easy-to-use mixins for developing a clean grid-based layout.

I hope I’ve provided a clear summary of how Stylus stacks up against the other big preprocessor players—and I also hope I’ve convinced you to give it a try.

Illustration by Erin Chan

The author

Image of Ashton Harris

Ashton Harris

Senior UX Engineer

Prepared by Intuitive Company before it joined Ernst & Young LLP.