Popular CSS Preprocessors With Examples: Sass, Less & Stylus
Posted May 2, 2018 | 13 min. (2611 words)CSS preprocessors are scripting languages that extend the default capabilities of CSS. They enable us to use logic in our CSS code, such as variables, nesting, inheritance, mixins, functions, and mathematical operations. CSS preprocessors make it easy to automate repetitive tasks, reduce the number of errors and code bloat, create reusable code snippets, and ensure backward compatibility.
Each CSS preprocessor has its own syntax that they compile into regular CSS so that browsers can render it on the client side. Currently, the three most popular and stable CSS preprocessors are Sass, LESS, and Stylus, however there are many smaller ones as well. CSS preprocessors all do similar things but in a different way and with their own syntaxes. Each of them has some advanced features unique to them and their own ecosystem (tools, frameworks, libraries) as well.
Sass: Syntactically Awesome Style Sheet
Sass is the oldest CSS preprocessor, initially released in 2006. Its creators, Natalie Weizenbaum and Hampton Catlin were inspired by the Haml templating language which adds dynamic features to HTML. Their goal was to implement a similar dynamic functionality in CSS as well. So, they came up with a CSS preprocessor and named it Syntactically Awesome Style Sheets. Sass allows frontend developers to use variables, if/else statements, for/while/each loops, inheritance, and other computational logic in their CSS code.
Sass is written in Ruby, and originally it also needed Ruby to compile the code, which deterred many developers from using it. However, the introduction of the LibSass library gave a huge boost to the utilization of Sass. LibSass is a C/C++ library that allows us to parse Sass in other backend languages, for instance in Node, PHP, and even C.
Syntax
Sass has two syntaxes. The .sass
file extension uses the older syntax that is indentation-based and omits semicolons and curly brackets from the code. The newer and more widely used syntax belonging to the .scss
file extension. It uses the standard CSS syntax with braces and semicolons.
Below, you can see a basic example of the Sass/SCSS syntax. The code simply declares two variables, $primary-color
and $primary-bg
and applies them to the body
HTML element.
/* SCSS */
$primary-color: seashell;
$primary-bg: darkslategrey;
body {
color: $primary-color;
background: $primary-bg;
}
The same code with the Sass syntax:
/* Sass */
$primary-color: seashell
$primary-bg: darkslategrey
body
color: $primary-color
background: $primary-bg
Both compiles to the same CSS:
/* Compiled CSS */
body {
color:seashell;
background: darkslategrey;
}
While the older Sass syntax is quicker to write and harder to get wrong, the newer SCSS syntax is fully compliant with the regular CSS syntax.
Features
In Sass, we can use variables to store values we want to reuse throughout the code. Sass variables are prepended with a $
sign, as you could see in the above example. Sass variables have scope so that we can use variables both locally and globally, which makes them quite versatile.
Sass follows the DRY (Don’t Repeat Yourself) programming principle in order to avoid duplication. It has two features that allow us to implement DRY: mixins and the @extend
rule.
Mixins make it possible to create a bunch of related CSS rules and quickly apply them to any property. For instance, the following mixin creates a simple card layout with width, height, background, and border as parameters.
/* SCSS */
@mixin card($width, $height, $bg, $border) {
width: $width;
height: $height;
background: $bg;
border: $border;
}
Whenever we want to create a new card, we simply call the card
mixin using the @include
rule and pass four arguments to it:
/* SCSS */
.card-1 {
@include card(300px, 200px, yellow, red 2px solid);
}
.card-2 {
@include card(400px, 300px, lightblue, black 1px dotted);
}
These two calls produce a smaller card with yellow background and red border, and a larger one with light blue background and black, dotted border:
/* Compiled CSS */
.card-1 {
width: 300px;
height: 200px;
background: yellow;
border: red 2px solid;
padding: 20px;
}
.card-2 {
width: 400px;
height: 300px;
background: lightblue;
border: black 1px dotted;
padding: 20px;
}
The @extend rule
brings inheritance to the Sass language. It’s incredibly useful when we have different design elements that share some characteristics. With the @extend
rule, we can add the properties of any class to another one in the following way:
/* SCSS */
.class-1 {
width: 100%;
height: auto;
}
.class-2 {
@extend .class-1;
}
The @extend
rule also extends all nested selectors, as Sass allows nesting as well. Nesting is a powerful part of the language, as it highly improves code readability and maintainability. It can be used when we have selectors that share the same parent, for instance:
/* SCSS */
article {
p {
line-height: 1.5;
}
img {
max-width: 100%;
}
}
Loops and conditionals are probably the most loved part of Sass, as they allow us to write CSS rules just like in any scripting language. Sass has a built-in if()
function and an @if
directive with which we can test different conditions and a @for
, an @each
, and a @while
loop with which we can repeatedly output specific sets of styles. With that many options, we can implement quite complicated logic within our CSS.
Sass supports modularity as well by letting us use partial Sass files that contain smaller code blocks we want to use many times, for instance a _reset.scss
stylesheet. Partials can be added to any other Sass file with the @import
rule.
The Sass preprocessor also has built-in functions we can use to convert and mix colors, manipulate strings, perform mathematical calculations, and apply other dynamic functionalities to our design. And, in case that wouldn’t be enough, we can define our own custom Sass functions as well.
Tools & Examples
Sass has an incredible ecosystem with an active developer community and many tools and libraries. The two most widely-used frontend frameworks, Bootstrap and Zurb Foundation are both written in Sass, which gives an extra traction to the language.
Moreover, Sass has powerful mixin libraries and authoring frameworks that further enhance the functionality of the language, such as Compass and Bourbon. There are several notable companies who use Sass in their production sites, for instance Airbnb, Kickstarter, Hubspot, Zapier, Freshbooks, and many others.
LESS: Leaner Style Sheets
LESS was initially released three years after Sass, in 2009 by Alexis Sellier. It was influenced by Sass, therefore it implements many of its features such as mixins, variables, and nesting. Interestingly, later LESS also influenced Sass, as the newer SCSS syntax was inspired by the syntax of LESS.
The LESS CSS preprocessor is, in fact, a JavaScript library that extends the default functionalities of CSS. As it’s written in JavaScript, we need Node.js to install and run the LESS compiler. However, we can also compile LESS on the fly by adding .less
files and the LESS converter to the <head>
section of our HTML page.
Syntax
LESS uses the standard CSS syntax with the .less
file extension. This means that a valid .css
file is a valid .less
file as well. Therefore, it’s really easy to pick up the syntax if you already know CSS, even if LESS has a few extra elements that you won’t find in CSS, such as the @
sign for variables.
For instance, the same example code we had a look in the Sass section above would look like this in LESS:
/* LESS */
@primary-color: seashell;
@primary-bg: darkslategrey;
body {
color: @primary-color;
background: @primary-bg;
}
Naturally, it compiles to the same CSS:
/* Compiled CSS */
body {
color: seashell;
background: darkslategrey;
}
LESS has a great documentation in which you can find everything related to its syntax, with detailed examples.
Features
Just like Sass variables, LESS variables also have scopes which make them accessible where they are defined and called. Moreover, they cannot only be used within CSS rules but also inside selector and property names, URLs, and @import
statements. For instance, we can store frequently used URL paths in variables and access them whenever we need to:
/* LESS */
@uploads: “../wp-content/uploads/”;
header {
background-image: url(“@{uploads}/2018/03/bg-image.jpg);
}
This compiles to the following background rule:
/* Compiled CSS */
header {
background-image: url("../wp-content/uploads/2018/03/bg-image.jpg");
}
In the example above, we can reuse the same @uploads
variable several times and never have to worry again about getting the URL path wrong.
LESS mixins work similarly to Sass mixins by letting us reuse a set of related style rules throughout the code. LESS also provide us with specific guarded mixins that implement a basic conditional logic in LESS. For instance, the following code example defines two distinct font colors (black and white) and applies them based on the lightness of the background. (The example also makes use of the lightness()
LESS function.)
/* LESS */
.text-color (@bg-color) when (lightness(@bg-color) >= 50%) {
color: black;
}
.text-color (@bg-color) when (lightness(@bg-color) < 50%) {
color: white;
}
.text-color (@bg-color) {
background-color: @bg-color;
}
.card-1 {
.text-color (yellow);
}
.card-2 {
.text-color (darkblue);
}
In the compiled CSS, the guarded mixin called .text-color
assigns black fonts to the first card with the yellow (light) background and white fonts to the second card with the dark blue (dark) background:
/* Compiled CSS */
.card-1 {
color: black;
background-color: yellow;
}
.card-2 {
color: white;
background-color: darkblue;
}
In fact, LESS doesn’t have as advanced conditional logic as Sass has (such as an if-else
statement), but we can still achieve a lot with mixin guards. Moreover, since version 1.5.0, we can also apply the when
keyword to CSS declarations, the same way we use mixin guards. Here is a very simple example from the docs:
/* LESS */
button when (@my-option = true) {
color: white;
}
LESS also provides us with several built-in functions with which we can manipulate colors, images, gradients, dimensions, units, and other characteristics.
The DRY principle and inheritance are integral parts of the LESS language as well, as we can use the :extend
pseudo-class to extend selectors and the merge feature to aggregate values from multiple properties. LESS also supports nesting so that we can easily achieve a readable and clear code base.
Tools and examples
Bootstrap’s decision to move Bootstrap 4 from LESS to Sass was a huge hit to the popularity of LESS, although it still has a LESS distribution. There are other LESS frameworks as well such as Cardinal and Semantic UI also has a LESS-only distribution.
LESS has some mixin libraries out there such as LESS Hat, however none of them are as powerful as Compass for Sass. There’s a great curated list of LESS tools on Github called Awesome LESS that’s worth having a look. We can also encounter LESS on production sites such as WeChat, Indiegogo, Transferwise, Patreon, Nordstrom, the Royal Opera House, and many others.
Stylus
The first version of Stylus was launched one year after LESS, in 2010 by TJ Holowaychuk, a former Node.js developer. Stylus is written in Node.js so that developers can easily integrate it into their Node projects. It was influenced by both Sass and LESS. Stylus combines the powerful logical abilities of Sass with the easy and straightforward setup of LESS.
Syntax
Developers frequently praise Stylus for its terse and flexible syntax. Stylus uses the .styl
file extension and it allows us to write code in many different ways. We can use the standard CSS syntax, but we can also omit brackets, colons, and/or semicolons or leave out all punctuation altogether. For instance, our previous code example looks like this using the different syntaxes of Stylus:
/* Stylus standard CSS syntax */
primary-color = seashell;
primary-bg = darkslategrey;
body {
color: primary-color;
background: primary-bg;
}
However, we can remove the brackets:
/* Stylus syntax without brackets */
primary-color = seashell;
primary-bg = darkslategrey;
body
color: primary-color;
background: primary-bg;
Or, we can remove the brackets and the semicolons, too:
/* Stylus syntax without brackets and semicolons */
primary-color = seashell
primary-bg = darkslategrey
body
color: primary-color
background: primary-bg
Or, we can remove all punctuation (brackets, semicolons, colons):
/* Stylus syntax without punctuation */
primary-color = seashell
primary-bg = darkslategrey
body
color primary-color
background primary-bg
However, we can’t remove the assignment operator (=), as it indicates the declaration of a new variable. As Stylus is a pythonic (indentation-based) language, we always need to pay attention to proper indentation, otherwise the code won’t compile.
Features
Stylus has the same basic features as Sass and LESS. Variables have a very clear syntax, we only need to pay attention to the equal sign; we don’t even need to prepend them:
/* Stylus */
primary-color = seashell
Stylus mixins work similarly to Sass and LESS mixins; we can use them to store and reuse custom style rule sets. However, transparent mixins are a unique feature in Stylus. They allow us to add automatic vendor prefixes to newer properties with insufficient browser support. For instance, we can create a transparent mixin for the experimental user-select
property and reuse it as many times as we want:
/* Stylus */
user-select(n)
-webkit-user-select: n
-moz-user-select: n
-ms-user-select: n
user-select: n
div
user-select(none)
We only need to add the user-select
transparent mixin to a CSS selector (div
), assign a value to it (none
), and Stylus generates all the necessary vendor prefix rules:
/* Compiled CSS */
div {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
Moreover, Stylus automatically adds vendor prefixes to keyframes, so it can be your ideal CSS preprocessor if you frequently build @keyframe
animations.
Stylus has several built-in functions to manipulate and convert colors and units, calculate average, minimum and maximum values, match patterns, and perform other actions. For instance, we can quickly find the grayscale equivalent of any color using the grayscale()
Stylus function:
/* Stylus */
grayscale(royalblue)
Just like with Sass, we can also build powerful custom functions with Stylus.
Stylus also provides us with powerful conditional logic, similar to Sass. It has an if/else/else if and an unless/else conditional statement. The unless
statement is the logical opposite of if
, we can think of it as an “if not” statement, too. With Stylus, we can also make use of postfix conditionals and looping that uses the for/in construct.
Stylus has many other advanced features developers frequently praise, such as property lookup that makes it possible to reference previous properties without assigning them to variables and partial reference that makes it possible to access only a certain number of levels of nested selectors.
Tools & Examples
Stylus has a handful of mixin libraries such as Nib and Ride.css that provide developers with ready-made cross-browser Stylus mixins. Although Stylus frameworks are not as famous as Bootstrap or Foundations, there are some good ones such as Basis and Kouto Swiss. The most notable corporate users of Stylus are Coursera, Livestorm, Accenture, Zenchef, HackerEarth, and a few others.
Conclusion
In fact, all CSS preprocessors have similar functionalities such as variables, mixins, importing, and nesting. They all follow the DRY principle and can perform conditional statements, functions, and operations. However, there are some important differences in their advanced features we need to think about before we choose one over the other.
Sass and Stylus are more like programming languages, as they have advanced logical and looping abilities and allow us to write custom functions. Moreover, Sass has a huge ecosystem and an active community, and popular frontend frameworks also use it. On the other hand, Stylus has an incredibly flexible syntax and can be easily integrated into Node projects. And while LESS has fewer logic-based features than Sass and Stylus, it can be easily compiled on the frontend which makes it great on a serverless architecture and has excellent built-in functions as well.