How to create a complex function in Stylus
I've been experimenting with Stylus a lot recently and each time I use it I learn more and more tricks. Below I'm going to show you how to create the calc() function without having to repeat it for each vendor prefix.
Add the prefixes
First store the vendors in a list.
vendors = webkit moz ms
Next iterate through each vendor to create the prefixes
for prefix in vendors
s('-%s-calc()', prefix)
The s() function allows us to combine our hyphens with the items in our array as a Litteral node. This means Stylus won't try to interpret the hyphens, but will simply replace %s with the prefix in the index.
Before we forget we should also include the non-prefixed version also.
for prefix in vendors
s('-%s-calc()', prefix)
s('calc()')
Create the function
Next we want to create a function to call this code every time we need to use it.
calc()
for prefix in vendors
s('-%s-calc(%s)', prefix, arguments)
s('calc(%s)', arguments)
To capture everything that's passed through the function we use the arguments local variable. This lets us do.
calc(30% - 10px)
However you'll notice that the expression inside the brackets gets calculated and that's not what we want for browsers to interpret the CSS function correctly. Therefore we need to pass our expression through surrounded in quotes.
calc('30% - 10px')
Now our function will preserve the expression but as a result it will also print the quotes. To prevent this we must pass the arguments local variable through the unquote() function.
calc()
for prefix in vendors
arguments = unquote(arguments)
s('-%s-calc(%s)', prefix, arguments)
s('calc(%s)', arguments)
Our function is starting to take shape. Unfortunately we can't use calc() on it's own. It must be used in conjunction with a property. Luckily Stylus has another local variable called current-property[0] that allows us to look up the current property and another one add-property() which allows us to repeat the property for each vendor prefix.
calc()
for prefix in vendors
arguments = unquote(arguments)
add-property(current-property[0], s('-%s-calc(%s)', prefix, arguments))
s('calc(%s)', arguments)
Go ahead and run your function inside a class
.test
width calc('30% - 10px')
Which should produce
test {
width: -webkit-calc(30% - 10px);
width: -moz-calc(30% - 10px);
width: -ms-calc(30% - 10px);
width: calc(30% - 10px);
}
Now I think that's pretty sweet.
Add error handling
For added reliability you might want to check the function is used within a property and output an error message if it's not.
calc()
if current-property
for prefix in vendors
arguments = unquote(arguments)
add-property(current-property[0], s('-%s-calc(%s)', prefix, arguments))
s('calc(%s)', arguments)
else
error('calc() must be used within a property')
And voila! You have a calc() function.
Written by Gavin McFarland
Related protips
4 Responses
This:
s('calc()', arguments)
Should be:
s('calc(%s)', arguments)
Thanks!
naorye http://www.webdeveasy.com
Good spot. Thanks @naorye
another tweak s('-%s-calc(%s)-'
has a trailing -
...and note to anyone who wants to use variables with this:
.content
height: calc('100% - %s' % headerHeight)
Silly me. Thanks for spotting that. :)