Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Using Yeoman with Compass Sprites

Yeoman + Compass Sprites

Setup

generator-webapp has support for compass out of the box. However, in order to use one of my favorite features of it — sprites and the image_url helper — you have to make some adjustments to the Gruntfile.

Let's assume you use a SASS stylesheet like this one:

@import "design/*.png"

.icon
    +design-sprite('icon')
    display: block
    width: design-sprite-width('icon')
    height: design-sprite-height('icon')
    
#logo
    background: transparent image_url('logo.png') no-repeat
    width: 64px
    height: 64px

This assumes that you have an icon.png file in your app/images/design/ folder and a logo.png in app/images/. In the default configuration, compass will generate incorrect paths to the file.

Gruntfile

To fix that, you have to make some adjustments to the Gruntfile.js:

compass: {
    options: {
        sassDir: '<%= yeoman.app %>/styles',
        cssDir: '.tmp/styles',
        imagesDir: '<%= yeoman.app %>/images',
        javascriptsDir: '<%= yeoman.app %>/scripts',
        fontsDir: '<%= yeoman.app %>/styles/fonts',
        importPath: 'app/components',
        // The next line tells compass where to put the sprites
        // and the HTTP path to them.
        raw: 'http_images_path = "/images/"\ngenerated_images_dir = ".tmp/images"\nhttp_generated_images_path = "/images/"',
        // This doesn't work with relative paths.
        relativeAssets: false
    },
    ...
},
imagemin: {
    dist: {
        files: [{
            expand: true,
            cwd: '<%= yeoman.app %>/images',
            src: '{,*/}*.{png,jpg,jpeg}',
            dest: '<%= yeoman.dist %>/images'
        }, {
            // Copy generated sprites over to the dist/
            // folder during the build step.
            expand: true,
            cwd: '.tmp/images',
            src: '{,*/}*.png',
            dest: '<%= yeoman.dist %>/images'
        }]
    }
},

Now you're done and should be able to use sprites both in server and production mode.

Caveats

The usemin has troubles detecting images referenced by the image_url helper and replacing them with the revved version, because compass appends a timestamp on them. You can either disable the cache buster feature of compass, or disable the rev task to work around this.

Good tip on usemin and asset_cache_buster :none.

badunk commented May 1, 2013

unfortunately, it seems like asset_cache_buster still doesn't remove the hash? This is according to the SO: http://stackoverflow.com/questions/9183133/how-to-turn-off-compass-sass-cache-busting/9332472#9332472

Is it advisable to have the Gruntfile config point to the actual config.rb file in order to implement the function recommended by the SO answer?

badunk commented May 1, 2013

Things I want to add in light of trying the SO answer above:

My compass config looks like this:

            options: {
                sassDir: '<%= yeoman.app %>/styles',
                cssDir: '.tmp/styles',
                imagesDir: '<%= yeoman.app %>/images',
                javascriptsDir: '<%= yeoman.app %>/scripts',
                fontsDir: '<%= yeoman.app %>/styles/fonts',
                importPath: 'app/components',
                relativeAssets: false,
                config: '.compass.rb'
            },

My .compass.rb config file:

http_images_path= '../images'
generated_images_dir = ".tmp/images"
http_generated_images_path = "../images/"
asset_cache_buster :none

on_sprite_saved do |filename|
  if File.exists?(filename)
    FileUtils.mv filename, filename.gsub(%r{-s[a-z0-9]{10}\.png$}, '.png')
  end
end

# Replace in stylesheets generated references to sprites
# by their counterparts without the hash uniqueness.
on_stylesheet_saved do |filename|
  if File.exists?(filename)
    css = File.read filename
    File.open(filename, 'w+') do |f|
      f << css.gsub(%r{-s[a-z0-9]{10}\.png}, '.png')
    end
  end
end

Finally, make sure you modify the default Yeoman Gruntfile's concurrency task. You want to move compass:dist out of concurrent so that compass sprites can be generated into app/images before executing imagemin:

        concurrent: {
            server: [
                'coffee:dist',
                'compass:server'
            ],
            test: [
                'coffee',
                'compass'
            ],
            dist: [
                'coffee',
                'imagemin',
                'svgmin',
                //'compass:dist', // moved this out
                'htmlmin'
            ]
        },
 grunt.registerTask('build', [
        'clean:dist',
        'useminPrepare',
        'compass:dist',
        'concurrent:dist', // added this here
        'requirejs',
        'cssmin',
        //'concat',
        'uglify',
        //'jshint',
        //'test',
        'copy',
        'rev',
        'usemin',
        'compress'
    ]);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment