Better CSS sprites

First of all I shall admit that current way of doing CSS sprites appears as a hack.

OK, what exactly is the CSS sprite? (actually sprite is a wrong name for the entity but seems like it is already wide spread, sigh )

Sprite (here) is just a fragment (slice, portion) of some base image. That image is loaded once but its fragments are used in various places as separate images (or pretended as being such).

Here is how sprites are done at the moment with standard CSS. As an example, typical use case – toolbar with list of buttons each having its own icon.

First we need to define common style for all our buttons:

  .toolbar li a { width: 25px; height: 25px; display: block; background:no-repeat url(tb-icons.png); }

And now for each button we need to define negative offset to scroll needed portion of the image into the “view” of current element like this:

.toolbar li a.btn-formatting { background-position: -25px 0; }
.toolbar li a.btn-bold       { background-position: -50px 0; }
.toolbar li a.btn-italic     { background-position: -75px 0; }
.toolbar li a.btn-font-size  { background-position: -125px 0; }

Because of negative image position button “font-size” for example is rendered like this:

So far so good as it works somehow.

But now try to imagine that one of your lucky users got some device with 225 DPI screen. So you will need to give him/her an image with larger icons for better experience. Images designed for 96dpi may look horrible scaled up to 225dpi by browser.

So you will end up with doing new set:

media ... {
  .toolbar li a { width: 25px; height: 25px; display: block; background:no-repeat url(tb-icons-x2.png); }
  .toolbar li a.btn-formatting	 { background-position: -41px 0; }
  .toolbar li a.btn-bold	 { background-position: -96px 0; }
  .toolbar li a.btn-italic	 { background-position: -143px 0; }
  .toolbar li a.btn-font-size    { background-position: -246px 0; }
}

All this is not that pretty and badly manageable – you need to recalculate positions each time. And there are many other problems with such “poor man” sprite approach. For example you cannot position the image in the middle of your button, everything is nailed down to the sprite size initially chosen.

Thus and so I’ve decided to make such image catalog mechanism better in Sciter2 engine by introducing @image-map at-rule and image-map() function. Here is how that toolbar declaration will look like with those two.

First of all here is our image map declaration:

      @image-map tb-icons 
      {
        /* we define three images under the single logical entity */
        src:   url(tb-icons.png) 120dpi,    /* <= 120dpi */
               url(tb-icons-x2.png) 240dpi, /* <= 240dpi */
               url(tb-icons-jumbo.png);     /* the rest  */

        cells: 15 2;                        /* 15 columns, 2 rows in the image */

        /* logical names of the parts, see tb-icons.png */ 
        items: bold, italic, underline, strike,
               font-family, font-size, text-color, text-back-color;
      }

And here is how they are used:

.toolbar > button {
  size:2em;
  background:no-repeat 50% 50%; padding:3px; /* note - middle aligned */
}

And use of particular items as just ordinary images using their logical names:

.toolbar > button.bold      { background-image:image-map(tb-icons,bold); } 
.toolbar > button.italic    { background-image:image-map(tb-icons,italic); } 
.toolbar > button.underline { background-image:image-map(tb-icons,underline); } 
.toolbar > button.strike    { background-image:image-map(tb-icons,strike); } 

Note that when you will need to support other resolution you don't need to redesign your CSS, just change the @image-map declaration.

And here is semi-formal specification of the image-map feature.