public
Last active

Using Yeoman with Compass Sprites

  • Download Gist
yeoman_compass.md
Markdown

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.

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?

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'
    ]);

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.