D-I-Y css sprite generation

Sun, May 1, 2011

In my previous post I announced the sprite-factory, a new ruby gem that makes it easy to generate CSS sprites… Strangely, I am now going to suggest that you might not need it!

The gem has multiple options for customization and is built to be flexible by abstracting:

  • how to layout the sprite
  • which stylesheet syntax to generate
  • which image library to use

It also comes with an extensive set of unit and integration tests to ensure that all these potential options do what they are supposed to do.

This allows the gem to be useful to a broad range of potential users.

YAGNI

However, for any single user, you are likely to use just a small subset of these options, and the abstractions are simply not needed.

In fact, if you only want simple horizontal sprites and traditional CSS syntax you could provide this functionality in about 30 lines of code:


  require 'RMagick'

  def sprite(input)

    images = Dir[File.join(input, "*.png")].sort.map do |filename|
      image = Magick::Image.read(filename).first
      { :image  => image,
        :name   => File.basename(filename)[0...-4], # strip .png extension
        :width  => image.columns,
        :height => image.rows }
    end

    width  = images.inject(0){|sum, i| sum = sum + i[:width] }
    height = images.inject(0){|max, i| max = [max, i[:height]].max }
    output = Magick::Image.new(width, height)
    css    = File.open(input + ".css", "w+")

    x = 0
    images.each do |i|
      y = (height - i[:height]) / 2 
      output.composite!(i[:image], x, y, Magick::SrcOverCompositeOp)
      css.puts <<-EOF
        img.#{i[:name]} {
          width: #{i[:width]}px;
          height: #{i[:height]}px;
          background: url(#{File.basename(input)}.png) #{-x}px #{-y}px no-repeat;
        }
      EOF
      x = x + i[:width]
    end
    output.write(input + ".png")
    css.close

  end

So what’s your point ?

Err, do I have one ? I’m not sure yet ;-)

Sometimes, programming best practices seem to compete with each other:

  • Understand your code - I think programmers should be intimately familiar with the code they are writing and using. Every 3rd party abstraction is an abstraction waiting to leak.

  • Don’t reinvent the wheel - If somebody already solved your problem then you should be re-using that knowledge, and where possible, that code.

I think generating CSS sprites is now a fairly well established performance best practice, and writing a tool to automate it is certainly not rocket science. It is something any competent programmer should be able to do within their own applications. However, that is the exact same reason why its probably an area of your code that you don’t really need to be intimately familiar with, and a 3rd party library, as long as it works, is likely to be something you can use and forget.

The one major benefit you get from using a 3rd party library instead of rolling your own is that it (should) come with unit tests, combine that with the fact that the more people using that library the more likely any bugs would have been worked out already. That alone will save you a lot of time writing your own tests.

Interestingly, the larger the feature, the more likely you would look to re-use an existing library, but perversely, that is exactly the case where you also need to spend the time to understand how that library works.

I think the point is, like every facet of programming, that ‘it depends’ on your particular case. Whilst its bad to fall into a “not invented here” syndrome and build everything yourself. Its equally easy to fall into a “never reinvent the wheel” belief and spend your time gluing together disparate chunks of code downloaded from ‘some guy on the internet’.

Hopefully, you can find a happy middle ground.

Ok, vague rambling over!

Generating CSS sprites is easy, whether you choose to use a library or to roll your own, you will benefit from the practice either way.