Experiences on the Front Lines of User Interfaces and Web Development

Generating CSS sprites for Rails with retina support - from Ruby

There is a nice Ruby gem that allows for automatic CSS Sprite generation: Sprite Factory. It has lots of ways to configure it, and allows for output that works with the Rails asset pipeline, which is nice.

Unfortunately out of the box it doesn't help with generating retina ready sprite files (i.e. with 2x size files where you then scale the image back - such as described here).

Here is the script I ended up with, using the Sprite Factory gem:

require 'sprite_factory'

SOURCE_PATH = "../Downloads/icon-assets-2x/"  
SELECTOR_PREFIX = ".icon"  
OUTPUT_IMAGE = "icons_sprite.png"

SpriteFactory.run!(SOURCE_PATH, {  
  library: "chunkypng",
  # go all lower case and convert spaces to underscores
  sanitizer: -> (name) { name.strip.downcase.gsub(/\s+/, '_').gsub(/[^A-z]/, '') },
  style: "scss",
  output_image: "app/assets/images/#{OUTPUT_IMAGE}",
  output_style: "app/assets/stylesheets/sprites/_icons.scss"
}) do |images|

  # Find the max right-most point of any sprite in the list.
  # This must therefore be the image width.
  image_width  = images.values.map { |data| data[:x] + data[:width] }.max

  output = <<-SCSS
    #{SELECTOR_PREFIX} {
      display: block;
      background-size: #{image_width / 2}px auto;
      background-image: image-url('#{OUTPUT_IMAGE}');
      background-repeat: no-repeat;
    }

  SCSS

  # The rest is standard, divide everything in half
  output << images.map do |name, data|
    <<-SCSS
#{SELECTOR_PREFIX}.#{name} {
  width: #{data[:width] / 2}px;
  height: #{data[:height] / 2}px;
  background-position: #{data[:x] / -2}px #{data[:y] / -2}px;
}
    SCSS
  end.join("\n")
end  

Original script that provided some guidance.

The output is really clean:

.icon {
  display: block;
  background-size: 1204px auto;
  background-image: image-url('icons/sprite.png');
  background-repeat: no-repeat;
}


.icon.calendar {
  width: 68px;
  height: 65px;
  background-position: 0px -27px;
}

.icon.trash {
  width: 68px;
  height: 63px;
  background-position: -68px -28px;
}

...
comments powered by Disqus