0
(0)

3 CSS preprocessors walk into a bar. Sass asks for a Tequila. The bartender says “You look like a Sassy fellow, here is your drink my friend”. LESS asks for a tap water, the bartender says: “Well my friend, less money, less benefits, right?” Then Stylus asks for Tequila Sunrise cocktail with rainbow coloured umbrella in it, the bartender goes: “Well guys, this guy is the most stylish one, isn’t he?”. All 3 get competitive hearing the bartenders words and LESS shouts “That’s it, tomorrow we will have a competition, we will see who’s the best ! This means war !”

So here we are on a job interview for SASS, LESS and Stylus and we will compare their strength and in the end we will draw a line with conclusions. Who gets the first prize ?

Note: We will refer to SASS on it’s middle name SCSS from now on.

“Alright guys, tell me 5 things that you can do and you are proud of!”

SCSS: “Listen up, I know variables and mixins with and without parameters. I also know nesting of selectors and parent selector and if-else conditions. I also know functions and loops like for-in and each. Who can top that? I’m listening?”

Interviewer: “Ok ok , settle down ! You know that CSS custom properties(variables) already exist so, you can scratch that from your list !

SCSS: “Hold on ! SCSS variables might not be better then CSS variables because they can not be changed dynamically on run-time because they are turned into flat css on compilation time and also they don’t have global scope and local scope, so if you reset a variable value in a selector it will overwrite the value on the globally set variable. But can CSS variables do inline conditions? I assume … NOT ! Let me explain:

@use "sass:color" as *;

$bgcolor: #fff;
$textcolor: if(lightness($bgcolor) > 40,black,white);
body {
  color: $textcolor;
}

SCSS: “As you can observe colour functions now can be retrieved as modules along math, map, list and meta. Also instead of @import the @use directive took over because with @use you just reuse the imported module as many times as you want but it is imported only 1 time. Also it is namespaced so if you use it without the as * you will have to reference lightness method for example like color.lightness or you can use an alias like @use sass:color" as c and then you reference it like c.lightness($bgcolor);

Anyway, in the example above we have a background color variable with a white color and in the textcolor variable we verify if the lightness of the $bgcolor variable is bigger the 40% then we can consider that it is bright enough and the text colour will be black for better contrast if not then text colour will be white. Pretty cool, huh?

Interviewer: “Yes, impressive! So tell me more about the other features”

SCSS: “Mixins are a pretty neat way of reusing a chunk of code multiple times with the @include directive and these mixins can have also parameters for us to be able to reuse them in different places but in each place with some variations, like different values for some of those properties:

@mixin flexCentered {
    display: flex;
    align-items: center;
    justify-content: center;
}
@mixin childLinkColor($color) {
   a {
     color: $color; 
   }
}
@mixin specialText($color, $fsize: 20px) { 
   p {
      color: $color;
      font-size: $fsize;
   }
}
@mixin isBigScreen($isLg) {
  @if $isLg {
    @media (min-width:1200px) { 
        @content 
    }
  }
  @else{
     @media (max-width:1999px) {
        @content
     }
  }
}

SCSS: “I created 4 mixins above and below we can see that the first included mixin does not have a parameter, no round brackets are used. The second has a parameter and also you can notice that there is a nested link so that will be nested inside the .box element. The 3rd include has a specified parameter and a missing parameter but we took care of that by adding a default value of 20px. The 4th mixin has conditions inside and a @content directive that will be replaced with the content we add when referencing the mixin:

.box {
   @include flexCentered;
   @include childLinkColor(green); 
   @include specialText(orange);
   @include isBigScreen(true) {
     padding: 20px;
   }
   margin: 10px;
}

Interviewer: “Alright, seems like you have a lot of freedom in doing all kinds of dynamic stuff. How about uniting already existing classes?”

SCSS: “I can do that also ! There is the @extend directive where I can just use what another class has or I can use a temporary element that I call Placeholder, that is not affecting the DOM, it is just holding data inside in order to be used later somewhere else.”

.list {
   text-indent: 20px;
   color: blue;
}
%bgcolor {
  background: green;
  &:hover {
    background: pink;
  }
  @media (min-width: 992px) {
    background: blue;
  }
}
.box {
   @extend .list;
   @extend %bgcolor;
}

SCSS: “The first one is a list class that will affect the DOM if there is a list class on an HTML element. The second one is a placeholder with the name bgcolor that is just a temporary element and is not parsed into CSS code. You could notice that I used a & sign, that represents the parent selector.Oh wait, there is more, I can do functions, maps and lists too and iterations between list or map items!

@use "sass:math" as m;
@function col-width($col/$total) {
   @return m.percentage($col/$total)
}

SCSS: “The difference between a mixin with arguments and a function is that functions have the @return directive inside returning a value. The above function will return a width in percentage for a certain grid system. This means if for example bootstrap is based on 12 columns and you need the width for 6 columns, you will divide 6 by 12 and to make it a percentage you will multiply it with 100 and add a % sign to it(that is done by the math functions by the way.”

Interviewer: “Ok let’s talk about iterations. How do you define lists and maps and how do you iterate through them?”

SCSS: “I will go directly to the examples part:

@use "sass:list" as l;

$sizes: 10vw, 50vw, 80vw;
$classes: small, medium, large;

@each $size in $sizes {
  $i: l.index($sizes, $size);
  .box-#{l.nth($classes, $i)} {
    font-size: $size;
  }      
}

SCSS: “I created 2 lists, one with font sizes and one with the variable part of a class name. Then iterated through the list in the sizes and stored in $i variable the index of the current list item(using the index() method from list module). Then I used <<interpolation>> with the classname string and the variable that holds the item from $classes at the current index.

$bgcolors: (
    primary: #ccc,
    secondary: #fff,
    accent: #000
);
@each $bgcolorkey, $bgcolorvalue in $bgcolors {
  .bg-#{$bgcolorkey} {
     background-color: $bgcolorvalue;
  }
}

SCSS: “The difference between lists and maps is that just like javascript objects it has a key and a value. When iterating through a map we can specify both key and value variables.”

“How about you LESS, can you compete with SCSS?”

LESS: “Well, I don’t have if-else conditions, but I have something authentic that I call Guards. Let me show it to you:

@bgcolor: #fff;
body {
  & when (lightness(@bgcolor) > 40) {
    color: black;
  }
  & when (lightness(@bgcolor) < 40) {
    color: white;
  } 
}

Interviewer: “Well that’s… ok. Let’s go further, how about mixins and functions?”

LESS: “What is cool about including a mixin is that you don’t have to use the @mixin and @include directive, you just add the reference and that’s it.We don’t have placeholders but mixins with round brackets are a good example for placeholders. The centered class is a mixin but it also applies to the DOM and it is used without the round brackets(this could be an extend as well). We can use also use the extend pseudo class on selectors or inside a selector. The all keyword means that also the nested selectors will be brought over to our new selector(in our case the <<a>> tag)

.centered-box {
   display: flex;
   justify-content: center;
   a {
     color:red
   }
}
.flexCentered() {
    display: flex;
    align-items: center;
    justify-content: center;
}
p {
   .flexCentered();
   color: #ccc;
}
div {
   .centered-box;
}

…here is an example with the extend pseudo class.”

.box:extend(.centered, p) {
     letter-spacing: 2px;
}
.box2:extend(.centered-box all) {
     letter-spacing: 3px;
}
.box3 {
    &:extend(.centered);
}

Interviewer: “Well I believe SCSS told us that extending a class will bring all nested elements by default, you don’t have to put an all keyword. Anyway, can you say something about maps, lists, iterations?”

LESS: “True, but we have shorter syntax, you don’t specify so much directives, you just write the code and we will know that this is a mixin or not. Regarding the list:

@colors: red, green, blue;

each(@colors, {
  .item-@{value}{
    color: @value;
  }
});

SCSS: “Hey LESS BOY ! You know that I have besides @each() also the @for() loop directive and @while?

@use "sass:math" as m;
@for $i from 1 through 12 {
    .col-#{$i} {
        flex: m.percentage($i/12);
    }
}

$count: 4;
@while $count> 0 {
  .box-#{$count} {
    &:before {
       content: '#{$count}';
    }
  }

  $count: $count- 1;
}

LESS: “Alright SCSS, you got more tricks up your sleeves then me when it comes to loops but don’t think that you are the only one with map()I can create maps too

@colors: {
  primary: red;
  secondary: green;
  accent: blue;
}
body {
  color: @colors[primary];
}

How about you Stylus? Can you top all this?

Stylus: “Hi all ! First off my variables are much simpler, the same story is with the mixins:

primary = #000;
body {
   color: primary;
}

flexCentered() {
    display: flex;
    align-items: center;
    justify-content: center;
}

.box {
   flexCentered();
}

Stylus: “We don’t have maps we have hash objects or hashes:

colors = {
  green: #00FF00 
  red: #f00
  blue: #099aab
}

for name, color in colors
  .box-{name}
    background: color;
    

Stylus: “Let me show you how we create a table with interpolations and for loop”

table
  for row in (1..10)
    tr.row-{row}
      background: #ccc

Stylus: “By the way LESS, I don’t think you mentioned functions, you probably don’t have any, but I do and I also do unit conversions just like css calc() method

sum(a, b)
  a + b
sum(5px, 10)  // result is 15px

Stylus: “I have also extends() method:”

.red { color: red }
.warning {
  @extend .red;
  padding: 10px
}

Stylus: “Interviewer…before you talk again let me tell you, I am modern and up to date with the customers wishes. I have rest params in the argument of the section, I have CSS literal if you can’t find a feature that css has you can always “open a portal” to CSS and write css code, I also have keyword arguments but let me show it to you:”

shadow(args...)
  box-shadow: args

body
  shadow 0 0 5px 10px #ccc, 1px 1px 5px 10px #000
@css {
   .box {
      filter: progid( DXImageTransform.Microsoft.Alpha(opacity=75)
   }
}
div {
   color: rgba(red: 100, green: 200, blue: 50, alpha: 0.5);
}

Final verdict

The interviewer analysed all 3 css preprocessors and came to a conclusion:

“Sadly LESS performed the worst between all 3 because it doesn’t have function() and for() loop and if-else conditions. The when guard kind of seems counter-intuitive. All in all LESS get’s the 3rd place. Now who get’s the first place that is a hard one. Let me elaborate !

SCSS is popular, it has a lot of features, inline conditions, maps, lists, mixins and function, a lots of built in functions and at least 3 options of looping through an iterable item on the other hand Stylus is more loose in language mixins are not specifically introduced with directives the same is valid for if else, it kind of felt easier to loose track of code.

The directives syntax is actually helping readability of the code. On the other side Stylus has extra features that nor LESS or SCSS has. I would say I tend to go with SCSS on the first place and Stylus on the second because SCSS feels more comfortable, more intuitive and more safe to write high level, complex code with for/each and conditionals and native functions altogether.

Thank you for reading the whole article and I hope you got the big picture about CSS preprocessors.

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.