Keep Sass Simple

In a couple of months, it will be 2 years since I started using Sass. Almost every day. At work. At home. On side projects. To the point where people ask for my help when it comes to Sass, and believe me I am very glad to provide help whenever I can.

Some requests are fair. Some are dead-simple but hey, we all started somewhere. And some are just insane. Look, I don’t judge; at some point every single person using Sass (or any other preprocessor for that matter) has been tempted to over-engineer something to make things simple™. Me included, and more than once.

Let’s Optimise!

The other day, I was asked how to do this:

.class {
  width: 20px;
}

.other-class {
  width: em(.class:width);
}

Basically, convert .class width to em then use it as the width of .other-class. Think about that for a second. Not only is px to em very difficult to achieve due to the lack of context in Sass, but now it is about referencing the value from the property of a selector as the value to a property from another selector. Nope.

Nope bear

Even Stylus — which is well known for being quite advanced — can’t do that. At best, it can refer to the value of the property in the same code block (known as property lookup). Obviously Sass — being much more conservative — can’t do that either.

But the problem is not about having the ability to do something like this. The problem is about wanting to do something like this. This should not happen. Ever. If you come up with a case where you need this, you’re obviously doing something wrong.

Note: if you ever wanted to do something like this, don’t feel ashamed or offended. This is just one example among many.

Because We Can

Okay, let’s agree on the fact that the above request is indeed a wrong way of using Sass. Do you want a more controversial example? The Sass 3.4 changelog recently got featured everywhere, promoting the incoming selector functions. Those features are meant to be able to manipulate selectors like lists, with functions like selector-nest(), selector-replace(), and so on.

No matter how hard I try, I have yet to find a single legitimate use case for selector functions. Some folks on Twitter tried to convince me with examples like this:

@mixin context($old-context, $new-context) {
  @at-root #{selector-replace(&, $old-context, $new-context)} {
    @content;
  }
}

li {
  float: left;

  ul {
    display: none;

    @include context('li', 'li:hover') {
      display: block;
    }
  }
}

While I agree it does the job in a clever way, I don’t think it’s simpler. I think it makes things more complicated. I think it adds code complexity where there should not be any.

Why not write it this way?

li {
  float: left;

  ul {
    display: none;
  }

  &:hover ul {
    display: block
  }
}

Now this is simple. This is comprehensible. I feel like sometimes we use things just because they exist, not because we should be using them. Also known as because-we-can syndrome.

How Did We Get Here?

In a way, I feel kind of guilty about this. I have made some crazy things with Sass (for example, here and here) and featured them, probably without warning enough about the fact that those techniques are mostly experimental.

I thought it was obvious enough. When you have to write dozens of lines of Sass to output a couple of lines of CSS, you should be suspicious. Excited and interested, yes, but still suspicious of using it in production.

It seems like if you give too much power to people, eventually they will abuse of it. Worse, they’ll probably want even more power. That’s how we are with CSS preprocessors. Variables were not enough, so we got mixins. And functions. And arrays. And more and more. And we didn’t stop to think about what we were doing, and why we were doing it.

I didn’t stop either, until I started sharing a code base with developers who had little to no experience with CSS. Over-engineering and crazy stuff was not an option anymore. And I am glad.

Should We Drop Sass Altogether?

That is not the point of this article, especially since there is nothing wrong with Sass. You know the saying:

Preprocessors do not output bad code. Bad developers do.

Sass is a wonderful tool, when you know how to use and how not to use it. Contrary to what some people think, there is absolutely nothing wrong with mixins or functions — even complex ones — as long as they don’t get too complicated. Complex is not complicated.

There is nothing wrong with nesting either, as long as you keep it under control. As far as I’m concerned, I tend to avoid nesting because it makes the code much harder to read.

While I love it when it comes to pseudo-classes and pseudo-elements, I think it can quickly get messy when nesting selectors within each other, like in the following example, taken from this post:

.tabs {
  .tab {
    background: red;
    &:hover {
      background: white;
      .tab-link {
        color: red;
      }
    }
    .tab-link {
      color: white;
    }
  }
}

Nope. Too much for me. I’d rather write:

.tabs .tab {
  background: red;

  &:hover {
    background: white;
  }
}

.tab-link {
  color: white;

  .tabs .tab:hover & {
    color: red;
  }
}

And you know what? The first example is 176 characters long while the second is only 152 characters long. So deep nesting does not necessarily save you characters.

But it’s Fun!

Yes it is. I know it is. I am the guy who wrote a JSON parser out of Sass, and implemented bitwise operators with nothing but SCSS. Of course it’s fun!

Over-engineering is not only fun, but also useful! I found some little bugs in Sass (#1090, #1265) while doing unexpected things. I also got better at Sass by doing unexpected things. You don’t get better by defining 3 variables per project. You get better by pushing the boundaries.

But you need to know where to stop. You need to know when things have gone too far, and you’re not fully in control of your code anymore. It took me almost 2 years and mostly a large-scale project to realize that. Everything is not meant to be used everywhere. Experiments don’t belong in a production environment. Not only this is wrong, but it actually can be quite dangerous.

Consider this hack I came up with to work around restrictions regarding cross media query @extend directives. It has been used in DoCSSa as — I quote — a DRY all the way approach. Indeed it is. Except it breaks the cascade. Because placeholders are generated and extended on-the-fly, your CSS gets moved here and there which might result in unexpected behaviour.

Me, when trying to figure out what's going on

That technique was an experiment. It was not meant to be used in a huge Sass framework that is supposed to solve everyone’s problems. This was not supposed to be used in production, at least not without being fully aware of the consequences. Yet, it is being used in that way.

Final Thoughts

Keep experimenting! Don’t stop hacking around Sass, because it is awesome. Just be careful with what you’re doing in real projects. Above all, keep things simple. Less is more.

Win an Annual Membership to Learnable,

SitePoint's Learning Platform

  • http://gregrickaby.com/ gregrickaby

    Brilliant post Hugo! KISS!

  • Thomas Semmler

    I can understand your argument about the context mixin, but I find this very useful. Imagine you work with BEM and you want to add a hover effect to the a “read more” text in a listed post, but as this article is listed, you wanna wrap the link around the whole article and not just the read-more-part. So you might end up with something like .post–listed__text__readmore. If you have applied all your styles to these selectors, then you find yourself inside .post { &–listed { @extend .post; &__text { &__readmore { } } } } (sorry, I know this is highly unreadable, but I am sure you get it anyway). In order to apply the animation to the “[...]__readmore”, youd have to go back to the &–listed and add a & > a:hover .post–listed__text__readmore. The context mixin avoids this and makes it, at least for me, a lot easier to write easy to understand BEM Code. But maybe you have a better workaround for this?

    • http://hugogiraudel.com/ Hugo Giraudel

      I don’t use nested notation for BEM as it makes it both harder to read and harder to search (since selectors doesn’t really exist as strings since they are built by Sass).

  • http://www.karlbrownvoiceover.com/ Karl Brown

    A great post, Hugo! I’ve deliberately avoided using SASS or SCSS because of precisely this; “It seems like if you give too much power to people, eventually they will
    abuse of it. Worse, they’ll probably want even more power.” Just because we *can* do something doesn’t mean we *should*.

    An extension of why you need to keep SASS simple is to avoid situations where things go wrong because of a simple error. I’ve talked with some developers who use preprocessors and haven’t ever picked up a pure CSS file, so they become so focused on “using SASS” that they forget that quite often what they want to achieve is perfectly easy in pure CSS and takes a fraction of the time to write.

    I do intend on learning different preprocessors as the need arises. I just hope by that point people always remember the advice: Keep It Simple, Silly!

  • seangore

    I mostly agree with these comments. These ideas definitely bring to mind my styleguide, which I recently posted on GitHub: https://github.com/srsgores/css-style-guide

  • johnsmartypants

    Your article is a very good example of why using preprocessors makes things more complex rather than improving things and ultimately damages productivity instead of simplifying it.

    • http://kleinfreund.de Philipp Rudloff

      Keep in mind that Hugo doesn’t state that either. It’s not as black and white as people want it to be, I believe. When done right, it can ultimately boost your productivity.

    • JesseWallace

      You meant to say “Your article SHOWS a very good example”. SASS is an unbelievably helpful tool as long as its used respectfully.

  • stefanjudis

    Greta post. One point though:

    > “stylus, which is well known for being quite advanced”

    Is it really like that? In my mind it’s less mature and in no point more advanced than sass. Maybe you may share your ideas about that?

    • benxamin

      I use NodeJS to require and compile various web components via node module or github repo. If you declare a component.json file, you can even require() these components inside a .styl file. When it comes to building and extending web components, I find Stylus far more convenient.

  • Matthieu Larcher

    Hi Hugo,

    I’m one of the developers behind the DoCSSa architecture. I fully agree on the KISS principle, and strive everyday to make it a reality. One could note though that we sometimes need to make some complex setups in order to have a simple to use implementation, or keep the css maintenance easy. As the saying goes, “Everything should be as simple as necessary, but no simpler”.

    You’ve came up on your blog with some pretty out-of-the-box-thinking Sass experimentations. Some of them were inspiring, some other left us wondering “why would we ever want to do that ?”. I’m glad to see you take them for what they are: experimentations.
    However, I’m a bit surprised that you take the cross media placeholders as an example. As long as we use it sparingly and know its limitation, it seems to me that
    it’s a decent workaround for a hindering problem in Sass.

    I’d be interested to know why you consider this particular experiment is not a good idea to implement. Would you mind commenting on this ?

    • http://hugogiraudel.com/ Hugo Giraudel

      It is just too complex, and most people simply don’t understand what it does behind the hood. Generating and extending placeholders on the fly can produce unexpected results, especially when you have no idea what’s going on.

  • Sean Lamberger

    Excellent points, all around. The can I / should I argument is always appropriate.

  • http://thewebbakery.co.uk Emma Davis

    Nice! For years I have been frustrated by some developer’s need to over-complicate their code (I think in a desire to make themselves look more clever) where much simpler and cleaner solutions are available and appropriate. I have only been using SASS for a couple of years and I too got carried away with nesting the first time round and quickly realised I was unnecessarily bloating my code instead of reducing it. This led me to question my use of SASS, but what I really love about it is the ability to create and re-use variables, mixins etc and so I stuck with it and now use it ‘properly’ – getting all the benefits without the bloat (only nesting when it really makes sense to do so) :)

  • http://www.aptugo.com Aptugo

    Preprocessors do not output bad code. Bad developers do.
    Love the phrase… but… developers running against the clock for a deployment deadline can output really ugly code. :)

    • http://hugogiraudel.com/ Hugo Giraudel

      Agreed.

  • http://www.lanarea.eu Ken Verhaegen

    Glad someone agrees, preprocessors are cool, but don’t overdo the magic!

  • Zl Qiu

    what do you think of this selector-modifier http://sassmeister.com/gist/306744db88c2b6deef78

    • http://hugogiraudel.com/ Hugo Giraudel

      It looks complicated. What problem are you trying to solve already?