Stylus: a new arrow for your design quiver

Why we chose the Node.js based CSS preprocessor and an introduction to using it to power up your CSS.

CSS preprocessors emerged in 2012 as mainstream tools to help designers and front-end developers manage increasingly complex user interface styles. SASS and {LESS} were joined by Stylus, a Node.js-based CSS preprocessor. Stylus is based on JavaScript, both in processor and language. However its syntax is almost Pythonic in its use of indentiation, something this Python shop embraced wholeheartedly.

Over the next few weeks, we’ll introduce various features of Stylus that we love, a few that we’d love to have or need improvement, and helpful code snippets as we discover them. In addition, we’ll unveil our own Stylus mixin sets, Vefa, that we’ve been using on various projects for the past year[1]. Also, a fair warning: we compile Stylus from the command line, or using the wonderful Codekit, rather than compiling it at runtime. There’s no discernible benefit to runtime compilation when you want the browser to cache the CSS anyway.

Coding style

Stylus allows you to code how you’re comfortable coding. Its very forgiving on how you set up your document and only cares that you adhere to a strict indentation and spacing style. So, all you one-liner CSS coders will have to break the habit and code in blocks like we all were originally taught!

When writing the Stylus, you can write the CSS however you are most comfortable with. You can write it as regular CSS:

.copy p {
     padding: 10px;
     font-size: 22px;
     color: blue;
}

You can drop the braces and semi-colons:

.copy p
     padding: 10px
     font-size: 22px
     color: blue

You can even drop the colons:

.copy p
     padding 10px
     font-size 22px
     color blue

We stick with the second style as it fits more in our headspace. Lots of people find option three too disconcerting, and after writing enough Stylus, I do too!

Stylus is smart. Or on second thought, maybe we can consider it dumb. Styles only looks for a few things in your declarations and everything is processed to a set of simple rules. If a declaration isn’t a Stylus keyword, it’ll process it based on its indentation. It’d be pro at XML because every line that has indented lines beneath it is considered a CSS selector. You can feed Stylus any selector or HTML element and it will consider that element legitimate. This is helpful for styling SVG elements and new HTML elements as they become available.

If a group of declarations has the same indentation and each has a value assignment, they are considered CSS declarations and processed as such. When using a nesting style, if the declaration does not have a value assignment and has a block of declarations after it at a new indentation level, its considered an selector and we start the whole process over again.

copy                         // CSS selector
     padding: 10px            // CSS style declaration
     background: grey         // CSS style declaration

     p                        // oh! a new CSS selector
          font-size: 22px     // CSS style declaration
          color: blue         // CSS style declaration
                              // doesn't care about this new line
     + form                   // back to old indentation, so processed as selector
          margin-top: 22px    // CSS style declaration

The above is output as:

.copy {
     padding: 10px;
     background: grey;
}

.copy p {
     font-size: 22px;
     color: blue;
}

.copy + form {
     margin-top: 22px;
}

One of the great things about Stylus being an idiot savant, is that you can output a lot of helpful information to your CSS to see various logic and value results. As long as you give a declaration with a value, Stylus will process it and spit out the result to the CSS:

.copy
     date: April 2 2013

Will show:

.copy {
     date: April 2 2013;
}

Why is this a cool and important feature? Because Stylus supports logic in its system!

CSS Logic

One thing I’d love to have in CSS is logic support: using if… then and switch statements and the like. Lucky for us, the CSS preprocessors fill the void nicely. Coupled with mixins and user functions, this can be a powerful tool. If you aren’t convinced, look at it this way: use the same Stylus file to support both a default style sheet and an Internet Explorer-centric one.

If we create 2 base stylus files, default.styl and ie.styl, we can set a variable flag in ie.styl:

ie.styl:
    supportsIE = true

We can then include another file, modules.styl, into each of the master stylus files.

ie.styl:
    @include "modules"

modules.styl:
    .copy p
         if supportsIE
             color: #0000FF
         else
              color: rgba(0, 0, 255, .6)      // rgba(red value, green value, blue value, alpha value)

When compiling, default.css will contain the second declaration with the nice rgba style, while ie.styl will show the regular hex value. Of course, this is very basic but you get the idea. I love the idea of having one master stylus file where I can declare both the general browser styles and IE-centric ones and let the compiler output to the correct file.

If you really want to stick it to IE, because we like other browsers better of course, you can always do:

modules.styl:
    .copy p
         unless supportsIE
              color: rgba(0, 0, 255, .6)      // rgba(red value, green value, blue value, alpha value)
         else
              color: #0000FF

or even do some inline logic to tighten things up:

modules.styl:
    .copy p
         color: rgba(0, 0, 255, .6) unless supportsIE
         color: #000000 if supportsIE

As you’ll see later on and through your own use, Stylus logic can really help us make the best CSS for our sites.

Overloading CSS

Remember how I said Stylus was an idiot savant and just processes everything on set rules, not caring too much about your coding style? Another awesome and helpful feature is overloading CSS properties and values.

The first is to overload CSS values, such as colors. Browsers understand various color values, such as blue and orange. But say you find that orange just a bit too much for your tastes. We can overload the orange value:

modules.styl:
    orange = #FFC014

and then everywhere that we have declared orange will instead get the new hex value.

modules.styl:
    .copy p
         color: orange

results in:

default.css:
    .copy p {
         color: #FFC014;
    }

An even greater piece is to overload a CSS property. Two that we overload in every project are margin and padding. We overload both to take their values, generally given in pixels, and return the values as REMs. Now, Internet Explorer 8 and below don’t understand REMs, so using the power of Stylus logic shown above, if supportsIE is declared, we just pass through the pixel values. So:

modules.styl:
    .copy p
         padding: 10px 20px

default.css (assume our REMs are set to 1rem = 10px):
    .copy p {
         padding: 1rem 2rem;
    }

ie.css:
    .copy p {
         padding: 10px 20px;
    }

You may have noticed that there was nothing special to declaring the overloaded declaration. We just wrote it like a regular declaration. Stylus will look for user-defined functions first and then process the remaining declarations and selectors after. Its smart about knowing when you’re calling a function. The great thing, too, is that this is completely transparent and you don’t need to write some convoluted functional declaration to get the results you want. Just write the Stylus like you would the CSS and voila! there it is.

Just so things are completely blackbox and magical: in our overloaded declarations, we use the Vefa rem() function to get the rem values.

Extending styles

I’m a stickler for not having class heavy HTML. I like to keep classes on elements to a bare minimum, usually a base layout class, a module class, and a modifier class if need be. We’ll save that for another post, but one thing that helps keep Stylus and CSS files sane is the @extend functionality[2]. @extend allows us to keep our code very modular and almost acts as a true inheritance model for CSS. When you declare an @extend for a selector, Stylus essential hoists that selector and applies it the same code block you are extending.

default.styl:
  .iso-1col
       padding: 10px

  .copy
       @extend .iso-1col
       color: #CCC
       font-size: 22px

default.css:
    .iso-1col, copy {
         padding: 1rem;
    }

    .copy {
         color: #CCC;
         font-size: 22px;
    }

We’ve kept our stylus code clean and modular, extending .iso-1col with .copy, or making .copy a descendent of .iso-1col, like we would in Python or other language (no, I know, not Javascript!). We often use throw away CSS IDs to act as abstracts and just extend like crazy off them!

Another cool thing about @extend is that you’re not limited to just CSS selector IDs or classes. You can get pretty complex with your extensions.

default.styl:
  .iso-1col
       padding: 10px

       .copy
            color: #ccc
            font-size:22px

            p
                 font-face: tahoma, arial, sans-serif
                 font-weight: 900

                 &:first-child
                      color: orange

  .callout
       @extend .iso-1col .copy p:first-child
       background: blue

defaults.css:
  .iso-1col {
       padding: 10px;
  }
  .iso-1col .copy {
       color: #ccc;
       font-size:22px;
  }
  .iso-1col .copy p {
       font-face: tahoma, arial, sans-serif;
       font-weight: 900;
  }
  .iso-1col .copy p:first-child,
  .callout {
       color: orange;
  }

  .callout {
       background: blue;

  }

@extend keeps your code clean and attempts to lessen both the amount of classes and the rewriting of CSS that you might otherwise have to write. Using @extend will help you write faster and will keep your head on straight when you code those 1500 line CSS files.

Conclusion

CSS logic, overloading CSS properties, and extending CSS declarations are just three helpful pieces in the Stylus preprocessor. Just using these three abilities will have you writing clean, logical CSS quickly in no time. I invite you to check out Learnboost’s Stylus and give it a whirl on your next project. Over the coming weeks, we’ll investigate ideas (and maybe a gripe, gotcha, or two) for Stylus, introduce our Vefa modules, and show you what Wellfire has been able to do when using Stylus.

[1] I’d hate to call Vefa a framework because its meant to be very open-ended and extensible for any developer to use. You’ll be able to use the pieces that you like, when you like, without having to worry about whether or not it relies on other pieces of the framework.

[2] Sass has something similar to @extends so you may be familiar with it.