-
Notifications
You must be signed in to change notification settings - Fork 2
Stylus Best Coding Practices
This is a curated set of conventions and best practices for Stylus, an expressive, dynamic, robust and advanced CSS preprocessor.
This is under improvement. Stay tuned.
Stylus is nice and welcoming. It's syntax is warm and forgiving. This friendly neighbourhood preprocessor does not differentiate between pure-CSS or indentation-based, bracketless code. It tries to be as transparent as possible, especially when it comes to mixins.
This is it's biggest flaw.
Not that Stylus is an overly-flawed preprocessor, it's actually quite the opposite. Out of all of the preprocessors I've tried, Stylus is - in my opinion; the most superior.
It's problem lies in the fact that because it's syntax is so forgiving, and there are so many different ways to write a block of Stylus code; it can be confusing to learn - it supplies the developer with no definitive direction. This, I postulate, is the main reason that it has not received as much attention as it should have.
The confusion doesn't stop at learning, though. Once you're engrained in variables, mixins and functions not requiring a prepended dollar sign ($) or 'at' symbol (@), you'll soon start to realize that you can no longer tell the difference between them. This makes for very confusing code - and this is only one example of Stylus' quirks.
This is why conventions and best practices need to be put in place (and I'm rather surprised it's taken this long!).
If you're wondering how to structure tidbits of your Stylus code, and one of the questions you have is "Camel Case, Underscores or Hyphenated?", then you need to examine HTML and CSS very closely.
Which of the following do you see more often in CSS?
a) linearGradient
b) linear_gradient
c) linear-gradient
If you chose 'C', then there's your answer. Try keep things hyphenated. It just makes sense.
Stylus supports near to anything you throw at it. You have the freedom of choice here.
If you prefer looking at neat code, this syntax is for you:
.selector
border-radius: 15px
border: 1px solid blue
If you're fond of your curly braces, then you can have them (and keep them, thanks):
.selector {
border-radius: 15px;
border: 1px solid blue
}
Stylus supports punctuated code as well as code that has an inherent lack of punctuation. It even allows you to remove colons. But you should not do this, as it makes for very unreadable code in large projects. It may be quick to type, but it is not quick to scan or read.
.selector
border-radius 15px // hard to read.
This convention splits off into two categories, and there are approximately the same amount of users for both conventions.
Stylus doesn't force you to declare variables with anything prepended to them - you simply define a variable like so:
my-variable = 'hey look, a variable!'
Naming mixins in Stylus is fairly straightforward, but eventually you might run into a problem that Jeff and I encountered in Axis. There's no snappy name for it (yet), but there is a rule to follow. The problem we encountered was with a mixin that Jeff created for rapid creation of navigation bars in Axis:
nav()
// pretend there's some code here
Can you spot the problem yet? No? C'mon - it's easy to see what's wrong here. It's just a mixin definition with nothing underneath it!
Still can't guess? Well - here's the thing; nav is an element that is already present in HTML5.
So, what does that mean for us? I'll tell you. Suppose, later on down the line, you want to style a ul inside a separate nav element...
nav ul
list-style-type: none
...And then things start to break. You get a confusing error message that makes no sense, and you don't know what to do. It took me three hours to learn what was actually going on. Stylus interpreted the nav selector as a mixin, and passed ul as a parameter. That's some pretty weird behaviour. So, quick tip:
Never name your mixins after HTML elements!
Stylus has two different levels of introspection, meaning that mixins and functions can be called at either the root or block level, and are capable of being aware of what level they were called at.
Block level mixins are called inside the body of a CSS selector. Root level mixins are called outside of that scope.
root-mixin() // a root level mixin
body
block-mixin() // a block level mixin
Root-level mixins and block-level mixins without arguments should always be called with parentheses. Block-level mixins with arguments should be called transparently, as if they were a CSS property:
foo() // root level w/ parens
body
bar() // no argments, must have parens
baz: 1px, 3px, 5px // has args, no parens
Folowing this particular convention will not only make your code more readable and maintainable, but it will also prevent a few rare compilation errors.
We've discussed most of the apparent conventions in Stylus - but what about functions? They're still kinda hard to differentiate from mixins...
Well, have a good look at Nib and a few other Stylus libraries. Notice anything?
I bet you did - it seems like most functions are prepended with a hyphen. Now, it's easy to tell mixins and functions apart!
-random-number()
return 4px // gauranteed to be random. Now look away ;)
body
font-size: -random-number()
Stylus supports two forms of conditional assignment. One of them is the recommended way to establish a global settings file.
I'll let you guess which one looks better in a conditional statement and which one looks more... settings-appropriate.
my-variable1 ?= red
my-variable2 := blue
If you guessed that the second one is better for settings files, then you were right.
Stylus didn't always use to have placeholder selectors, and there were some conventions regarding this. But now, it does have placeholder selectors. They are prepended with a dollar sign.
$placeholder
background: red
.red-thing
@extends $placeholder
Sometimes, when you're working with dynamic content - you might have a selector, inside a selector, inside another selector. Let's assume they have a class of .a, .b and .c, respectively.
You want to select .c, but .b is not always present on the page. Using the Stylus parent selector (&), you can still adjust your stylesheet to be aware of that:
.a
> .b, &
> .c
color: red
Now, even if .b is not present, .c will still get styled appropriately.