-
Notifications
You must be signed in to change notification settings - Fork 6
Workflow
Generally all image spriting tools can be divided into two broad categories:
- Converters of existing projects.
- Supporters of a development process.
grunt-tight-sprite
is in the second category.
Converters typically deal with existing assets (images, CSS, HTML), and can parse files, detect used images, sprite them, and edit existing CSS and HTML to use sprites.
There are numerous technical problems with this approach:
- Parsing is hard, and consequently error-prone. Many tools don't parse HTML, opting for manual removal of images, and replacing them with CSS backgrounds before any conversion.
- Some tools have problem with parsing CSS properly, and require either a proper formatting, or a manual mark-up with pseudo-comments in order to extract needed information.
- Editing of existing CSS and HTML is hard, so many tools opt for producing new files, which should be manually integrated into the application.
- Incorporating new images, and deleting old ones begins usually with manual editing of a CSS file.
- Separating images to different sprites is complicated. It is not recommended to mix images of different types in a single sprite (e.g., photos vs. sharp graphics) due to different compression requirements (e.g., JPEG vs. PNG). Common solutions are separating images manually into different CSS files and processing them differently, or using a special mark-up with pseudo-comments.
These tools (and grunt-tight-sprite
) should be used routinely during the whole life-cycle of a project.
Common workflow:
- Artist produces images. Typically they have self-evident unique names. Their names, and their folders may already encode some pertinent information. For example, they can be grouped in folders by their size (e.g., icons 32 by 32), or by their functional purpose (e.g., corporate logos). The names and folders are used by an artist and a programmer to refer to images unambiguously.
- Programmer places images into their proper location in a project folder, and run a tool, which processes images, and produces a corresponding CSS file with automatically generated unique CSS class names for every image. Programmer uses those class names to style web pages.
- Artists modifies existing images, produces new ones, or removes old ones, and the cycle repeats.
Obviously a "converter" is geared towards working on legacy projects. From my experience a conversion is far from free and requires extensive modifications to the project rendering this advantage practically moot.
On one project I spent a lot of time converting it to use sprites. This is the process listed here for posterity:
-
Find and replace all
<img src="images/image.jpg">
elements in HTML files and templates with<div class="sprite_image"></div>
. This step produced a lot of new CSS classes like this:.sprite_image { background-image: url(../images/image.jpg); background-position: -100px -200px; background-repeat: no-repeat; width: 32px; height: 32px; display: inline-block; }
-
Decide what images should go into a sprite, and which shouldn't for technical or business reasons. For example, some big images, dynamically generated images, or frequently modified images should be skipped. They should go into separate CSS files.
-
Finally I am done, and can run the tool. The result was horrible, even worse than it was before especially for mobile browsers.
-
Postmortem: the resulting sprite was too big on two accounts:
- It was a bigger file size-wise than all images before combined.
- The resulting image was so big pixel-wise, it crashed an image previewer.
The first one was due to different compression needs of different images (e.g., photos vs. graphics), while the second was due to a sub-par packing, which produced a lot of unused space.
-
In order to combat the first problem, I had to separate images into two broad categories:
- better compressed with JPEG.
- better compressed with PNG.
That helped tremendously with the transmitted file size. I was fully prepared to produce several sprites with different JPEG quality settings for different image asset classes, but I was lucky that one JPEG sprite was good enough. Increasing number of sprites can defeat their purpose to reduce a number of required network connections, so it is not a decision to take lightly.
-
In order to combat the second problem I had to "help" the algorithm manually, and sorted all images into two piles: small ones, and big ones. Having more or less homogeneous images in each pile increased a number of sprites, but greatly reduced the waste.
After more profiling, and analysis, I ended up with three sprites: a JPEG one, a PNG one with big chunks,
a PNG one with small chunks. Given that they replaced hundreds of individual images, the result was
tremendously positive. But during the process I touched all HTML files in the project, and heavily edited
all CSS files. In order to achieve what I wanted (3 sprites) I had to rearrange CSS, add
pseudo-comments, so the tool could assign images to corresponding sprites, and reformat CSS,
which was not understood by tool (e.g., background
should be split to background-image
,
background-position
, background-repeat
, and so on).
"Process" tools require at least the same amount of work for existing projects, but
greatly reduce friction, when programmers count on them, and incorporate them as
a part of their process. This is where grunt
comes into the picture. The ideal situation
is to start using grunt-tight-sprite
from the beginning.
Usually the biggest obstacle for such tools is to use automatically generated CSS class
names persistently, which means that image files should be named properly, e.g.,
'chevron-logo.png'
instead of 'lg4155.png'
. With the "converter" tool all CSS class
names are assigned manually, and can look nice. But it makes communicating with teammates
about images hard, because every time, we need to look up both CSS class and file names.
Clear names should be enforced at the source, and used consistently through the project.
If you agree with that, you are ready to try "process" tools.
How to implement the "process" workflow described above with grunt-tight-sprite
?
One possible scenario:
- Let's imagine the result first:
-
When we need to place an image, instead of
<img>
we will use<div>
like that:<div class="sprite sprite_logo"></div>
-
It will work assuming we have a following CSS in place:
.sprite { background-image: url(../images/sprite.png); background-repeat: no-repeat; display: inline-block; } .sprite_logo { background-position: -120px -168px; width: 64px; height: 64px; }
-
We separated static and dynamic CSS properties into two different CSS classes. Now we have to generate
.sprite_logo
, and other CSS classes for all our images. -
Now imagine that our artists already put all graphics into
pictures/
sub-folder, and all photos intophotos/
sub-folder. We can use this information to produce two different sprites. (Obviously we can split files differently, e.g., using a file extension as a hint, or sometimes this information is encoded into a name as a prefix/suffix of some sort -- we can dogrunt
facilities to select proper files with wildcards.)
-
- Now it is time to write some code:
-
Let's add CSS rules for both sprites in an appropriate CSS file:
.sprite-p { background-image: url(../images/sprite-p.png); background-repeat: no-repeat; display: inline-block; } .sprite-j { background-image: url(../images/sprite-j.jpg); background-repeat: no-repeat; display: inline-block; }
-
Let's write a template for our images, and save it as
sprite.tmpl
next to ourgruntfile
:.<%= className => { background-position: -<%= x %>px -<%= y %>px; width: <%= w %>px; height: <%= h %>px; }
-
Now it is time to write a
Gruntfile.js
:"use strict"; var imagePath = "assets/images/"; module.exports = function(grunt) { grunt.initConfig({ tight_sprite: { sprite_p: { options: { cssDest: "assets/css/sprite-p.css", templateFile: "sprite.tmpl", hide: imagePath + "pictures/" }, src: [imagePath + "pictures/**/*.{png,jpg,jpeg,gif}"], dest: imagePath + "sprite-p.png" }, sprite_j: { options: { cssDest: "assets/css/sprite-j.css", templateFile: "sprite.tmpl", hide: imagePath + "photos/", jpeg: { quality: 85 } }, src: [imagePath + "photos/**/*.{png,jpg,jpeg,gif}"], dest: imagePath + "sprite-j.jpg" } } }); grunt.loadNpmTasks("grunt-tight-sprite"); grunt.loadTasks("tasks"); grunt.registerTask("sprite", "tight_sprite"); };
Obviously real project will have more tasks defined.
-
- Now whenever we modified our images we just run
grunt sprite
, and everything will be updated automatically provided that we were consistent with our use of CSS classes, andsprite-p.css
andsprite-j.css
are properly included in HTML files. - Profit! :-)
Do not forget to check common recipes listed on the main Wiki page. If you stuck, or have some questions, please check FAQ, which is constantly updated to cover more and more ground.