SeanColombo.com

My little corner of the internet.

How to make (and use) a custom SASS function

The SASS (Syntactically Awesome StyleSheets) language is pretty neat. For my first use of SASS, I realized that one of the requirements for our system wasn’t easily supported by the language… but SASS is written in Ruby which is really easy to extend. The docs even mentioned the ability to create custom functions. However, I couldn’t find any docs on how to actually create and use a custom SASS function, so I figured I’d give a quick tutorial here of what I learned. This method is probably obvious to hardcore Ruby users, but I’d never used Ruby before. Turns out it’s pretty quick to learn. If you want to do the Matrix thing and pump the whole language into your brain like Neo in a ghetto dentist-chair, check out this Ruby crash course.

NOTE: This tutorial is targeted primarily at people using SASS via the command line (for PHP, Java, etc.), not as a Rails module.

My example: getting values from the sass command-line

I’m using SASS in a PHP environment (rather than Rails) and due to unique requirements, I need to be able to configure certain values in .scss at ‘compile’ time (referring to when the .scss is being compiled into .css).

One simple trick would be to simply write out a .scss file containing the key-value pairs. Unfortunately, the system I need to use SASS for already has tens of millions of page-requests per day so disk-writes would be a huge bottleneck (because disk i/o – even on solid state disks – is slow compared to many other methods). Custom SASS functions provided the perfect opportunity to completely skip this step. And yes: pre-generating all of the CSS files at code-deployment is out of the question because the number of possible stylesheets we need to support is intractably large.

The custom function

To create your custom SASS function, make a ruby file. We’ll call it sass_function.rb in this example. In the file, you need to define your function and then insert your module into SASS. Behold!


require 'sass'

module WikiaFunctions
        def get_command_line_param(paramName, defaultResult="")
                assert_type paramName, :String
                retVal = defaultResult.to_s

                # Look through the args given to SASS command-line
                ARGV.each do |arg|
                        # Check if arg is a key=value pair
                        if arg =~ /.=./
                                pair = arg.split(/=/)
                                if(pair[0] == paramName.value)
                                        # Found correct param name
                                        retVal = pair[1] || defaultResult
                                end
                        end
                end
		begin
			Sass::Script::Parser.parse(retVal, 0, 0)
		rescue
			Sass::Script::String.new(retVal)
		end
        end
end

module Sass::Script::Functions
  include WikiaFunctions
end

The particular SASS function in this example takes in name/value pairs defined on the sass command line and returns them if they’re there (or an optional default otherwise).

Calling SASS

Since this tutorial assumes that you’re using sass from the command-line, you’ll have to tweak the command a little bit to tell ruby to use your new module. Here is a simple example:
sass unicorn.scss unicorn.css -r sass_function.rb
That doesn’t make use of the awesomeness of our new, command-line parsing function though! So here is an example that WOULD make use of it:
sass unicorn.scss unicorn.css logoColor=#6495ED -r sass_function.rb

So now we have a function capable of reading the command-line and a command-line with some useful information in it. Now all that’s needed is some SASS code (.scss) to make use of all of that.

In this example, we’ll set the “logo” element to have a background-color that we get from the command-line (and default to white if no matching value is passed in on the command-line). Remember: this would go in SASS code such as unicorn.scss


$logoBackgroundColor: get_command_line_param("logoColor", "white");

#logo{
   background-color: $logoBackgroundColor;
}

Now we have all of the pieces:

  1. The custom SASS function (called get_command_line_param()) in sass_function.rb
  2. The code in unicorn.scss to use our function to set the style by command-line info.
  3. The command-line needed to include our custom code and to set the logoColor value.

So if we run
sass unicorn.scss unicorn.css logoColor=#6495ED -r sass_function.rb
we will have a unicorn.css which contains something like:


#logo{
   background-color: #6495ED;
   }

Just what we were going for! If you try this out, let me know in the comments if it worked for you or if you have any questions.

Best of luck!

Special thanks to Nathan Weizenbaum for pointing me down the right path with this stuff. Updated on 20100729 to change the return-value of the function based on the helpful comments below. Thanks!

Category: Programming
Tag: , , , ,
  • Chris Eppstein says:

    If you change the last line from:

    Sass::Script::String.new(retVal)

    to:

    Sass::Script::Parser.parse(retVal, 0, 0)

    then the object returned will be parsed as a sass value object. E.g. 2px would be a number object and #fff would be a color object. This will allow the returned values to participate correctly in expressions. If you’re passing values that don’t parse, you could pass them with quotes or rescue the parse error and return a string.

    Compass users: you can just place a snippet like this into your compass config.

    July 28, 2010 at 11:34 pm
  • will says:

    Here’s a more idiomatic ruby way to do that method:

    
    def get_command_line_param(param_name, default_result='')
      result = ARGV.find {|arg| arg =~ /^#{param_name}=/} || default_result
      Sass::Script::String.new result.split(/=/).last 
    end
    
    July 29, 2010 at 1:58 am
  • Sean says:

    Awesome! Thanks for the tips, guys.

    @Chris: I updated the post to use this return value… great idea!

    @Will: Nice! I’ll probably use that in my actual code (with the Parser.parse change from Chris also).

    Thanks again!

    July 29, 2010 at 1:32 pm
  • Sean says:

    @Chris: Now I see what you mean by values that don’t parse ;) I tried to pass in an image (eg: “colombo.jpg”) and it choked a bit. Added the ‘rescue’ like you recommended and it works great!

    Thanks yet again! :)

    September 9, 2010 at 8:12 pm
  • Raina Gustafson says:

    I’ve only messed around with SASS as a part of Compass, but now I’m thinking about it again… There could definitely be a place for it in our workflow.

    Can you explain more specifically what you’re gaining with this approach and how it fits into your overall code management/deployment strategy? Are all your developers committed to using SASS, and if not, how do you manage the code base amongst the differing development practices?

    November 23, 2010 at 5:31 pm

Your email address will not be published. Required fields are marked *

*