How to use SVG Sprite Sheet as CSS background-image while maintaining aspect ratio and scalability


Tags: css,svg

Problem :

TL;DR: I want to use several icons tiled in an SVG sprite sheet as CSS background-images, which maintain their aspect ratio and automatically scale to fill the parent element, using nothing but SVG and CSS. No JavaScript please.


So I have a spritesheet in SVG format, which I made with a combination of SVG-Edit and some hand-coding in Notepad++. Here's the source code:

<svg version="1.1"
  xmlns:svg="http://www.w3.org/2000/svg"
  xmlns="http://www.w3.org/2000/svg"
  width="600"
  height="400"
  viewBox="0 0 600 400">
  <!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
  <title>chosen_sprite</title>
  <g>
    <title>Add</title>
    <rect fill="none" stroke-width="10" stroke-dasharray="null" stroke-linejoin="null" stroke-linecap="null" x="5" y="5" width="90" height="90" id="svg_1" stroke="#dcdcdc"/>
    <line id="svg_2" y2="50" x2="70" y1="50" x1="30" stroke-linecap="round" stroke-linejoin="null" stroke-dasharray="null" stroke-width="12" stroke="#00a00c" fill="none"/>
    <line id="svg_3" y2="30" x2="50" y1="70" x1="50" stroke-linecap="round" stroke-linejoin="null" stroke-dasharray="null" stroke-width="12" stroke="#00a00c" fill="none"/>
  </g>
  <g>
    <title>Delete</title>
    <rect fill="none" stroke-width="10" stroke-dasharray="null" stroke-linejoin="null" stroke-linecap="null" x="105" y="5" width="90" height="90" id="svg_1" stroke="#dcdcdc"/>
    <line id="svg_2" y2="70" x2="170" y1="30" x1="130" stroke-linecap="round" stroke-linejoin="null" stroke-dasharray="null" stroke-width="12" stroke="#ff0000" fill="none"/>
    <line id="svg_3" y2="30" x2="170" y1="70" x1="130" stroke-linecap="round" stroke-linejoin="null" stroke-dasharray="null" stroke-width="12" stroke="#ff0000" fill="none"/>
  </g>
  <g>
    <title>Expand Dark</title>
    <rect stroke="#505050" id="svg_1" height="90" width="90" y="5" x="205" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="10" fill="none"/>
    <line fill="none" stroke="#000000" stroke-width="12" stroke-dasharray="null" stroke-linejoin="null" stroke-linecap="round" x1="250" y1="65" x2="280" y2="35" id="svg_2"/>
    <line fill="none" stroke="#000000" stroke-width="12" stroke-dasharray="null" stroke-linejoin="null" stroke-linecap="round" x1="220" y1="35" x2="250" y2="65" id="svg_3"/>
  </g>
  <g>
    <title>Collapse Dark</title>
    <rect stroke="#505050" height="90" width="90" y="5" x="305" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="10" fill="none" id="svg_4"/>
    <line fill="none" stroke="#000000" stroke-width="12" stroke-dasharray="null" stroke-linejoin="null" stroke-linecap="round" x1="350" y1="35" x2="380" y2="65" id="svg_5"/>
    <line fill="none" stroke="#000000" stroke-width="12" stroke-dasharray="null" stroke-linejoin="null" stroke-linecap="round" x1="320" y1="65" x2="350" y2="35" id="svg_6"/>
  </g>
  <g>
    <title>Expand Green</title>
    <rect fill="none" stroke-width="10" stroke-dasharray="null" stroke-linejoin="null" stroke-linecap="null" x="405" y="5" width="90" height="90" id="svg_1" stroke="#dcdcdc"/>
    <line id="svg_2" y2="35" x2="480" y1="65" x1="450" stroke-linecap="round" stroke-linejoin="null" stroke-dasharray="null" stroke-width="12" stroke="#00a00c" fill="none"/>
    <line id="svg_3" y2="65" x2="450" y1="35" x1="420" stroke-linecap="round" stroke-linejoin="null" stroke-dasharray="null" stroke-width="12" stroke="#00a00c" fill="none"/>
  </g>
  <g>
    <title>Collapse Green</title>
    <rect fill="none" stroke-width="10" stroke-dasharray="null" stroke-linejoin="null" stroke-linecap="null" x="505" y="5" width="90" height="90" id="svg_1" stroke="#dcdcdc"/>
    <line id="svg_2" y2="65" x2="580" y1="35" x1="550" stroke-linecap="round" stroke-linejoin="null" stroke-dasharray="null" stroke-width="12" stroke="#00a00c" fill="none"/>
    <line id="svg_3" y2="35" x2="550" y1="65" x1="520" stroke-linecap="round" stroke-linejoin="null" stroke-dasharray="null" stroke-width="12" stroke="#00a00c" fill="none"/>
  </g>
  <g>
    <title>Search</title>
    <circle id="svg_9" r="32" cy="140" cx="60" stroke-width="8" stroke="#000000" fill="none"/>
    <line id="svg_11" y2="167.5" x2="32.5" y1="190" x1="10" stroke-linecap="round" stroke-linejoin="null" stroke-dasharray="null" stroke-width="12" stroke="#000000" fill="none"/>
  </g>
  <g>
    <title>Search 2</title>
    <rect id="svg_10" stroke="#505050" height="90" width="90" y="105" x="105" stroke-linecap="null" stroke-linejoin="null" stroke-dasharray="null" stroke-width="10" fill="none"/>
    <circle r="25" cy="142.5" cx="157.5" stroke-width="8" stroke="#000000" fill="none" id="svg_7"/>
    <line y2="165" x2="135" y1="180" x1="120" stroke-linecap="round" stroke-linejoin="null" stroke-dasharray="null" stroke-width="12" stroke="#000000" fill="none" id="svg_8"/>
  </g>
</svg>

It works fine and looks the way I want it to.

The problem is the CSS. Defining the cells in the spritesheet is a little bit messier than I would like it to be. Here's the page I'm displaying these icons in:

<!DOCTYPE html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<html>
<head>
<style>

* {padding: 0px; margin: 0px; outline: 1px solid rgba(0,0,0,0.1);}

html {width: 100%; height: 100%;}

body {width: 100%; height: 100%;}

.svgSprite {
    background-image: url('./svgicons/form_icons_sprite.svg');
    background-repeat: no-repeat;
    background-size: 600%;
}

.svgSprite.add {
    background-position: 0px 0px;
    width: 12px;
    height: 12px;
}

.svgSprite.delete {
    background-position: -16px 0px;
    width: 16px;
    height: 16px;
}

.svgSprite.expandDark {
    background-position: -24px 0px;
    width: 12px;
    height: 12px;
}

.svgSprite.collapseDark {
    background-position: -36px 0px;
    width: 12px;
    height: 12px;
}

.svgSprite.expandGreen {
    background-position: -48px 0px;
    width: 12px;
    height: 12px;
}

.svgSprite.collapseGreen {
    background-position: -60px 0px;
    width: 12px;
    height: 12px;
}

.svgSprite.search {
    background-position: 0px -12px;
    width: 12px;
    height: 12px;
}

.svgSprite.search2 {
    background-position: -16px -16px;
    width: 16px;
    height: 16px;
}

</style>
</head>

<body>
<div class="svgSprite add"></div>
<div class="svgSprite delete"></div>
<div class="svgSprite expandDark"></div>
<div class="svgSprite collapseDark"></div>
<div class="svgSprite expandGreen"></div>
<div class="svgSprite collapseGreen"></div>
<div class="svgSprite search"></div>
<div class="svgSprite search2"></div>
</body>

</html>

Basically, I want to know if there's an easier way to define the cells in the spritesheet and simplify the CSS I use to tell each div which icon to display from the spritesheet.

I would prefer that this solution be strictly SVG and CSS; I am not interested in using JavaScript libraries. I am aiming to get it to a point where I can simply define the cells and have the particular icon I'm aiming for automatically scale to fit its container, while maintaining its aspect ratio. Currently, in order to make the icon fit its parent container, its width and height need to be explicitly defined, and match the width and height of the parent container. If I change the width and height of the parent container, I need to change the background-position sizes as well.

Then, there's the problem of scaling. With this setup, the SVG scales to the appropriate size to be drawn on-screen, but if I decide to zoom using my browser's zoom, it pixelates. This is not how SVG is supposed to work.

I suppose I could just put each icon in its own file, because that seems to work wonderfully, but I just really like using sprites; it not only saves me several server requests, it's just cool.

I am aware of SVG Icon Loader. It's pretty cool, but it's one more JavaScript file that I would rather not rely on.

I've already read the w3 SVG docs, the MDN SVG docs, and the following threads on SO:

SVG & Spritesheets

Fit <svg> to the size of <object> container

Using SVG as background image

...but even after all that, I haven't managed to find a solution.

EDIT: I forgot to mention, this needs to work in IE9. That's a bit of an issue, I'm sure, but IE9's SVG support is decent, which is why I chose SVG for this project.



Solution :

Basically, I want to know if there's an easier way to define the cells in the spritesheet and simplify the CSS I use to tell each div which icon to display from the spritesheet.

No, you can't do it easier.

Try this article

Then, there's the problem of scaling. With this setup, the SVG scales to the appropriate size to be drawn on-screen, but if I decide to zoom using my browser's zoom, it pixelates. This is not how SVG is supposed to work.

In Chromium 18 it looks pretty fine - no pixelations at all.

In my test browsers list (FF3.6 Opera 9.2 IE6) I didn't see what I saw in Chromium

And about IE9, maybe problem in engine


    CSS Howto..

    How to make dynamic slideshow using php, mysql, css and html?

    How can I resize a background-image to fit my element (without set width) in CSS 2?

    How to load local copy of bootstrap css when the cdn server is down?

    How to make an overlay using css hover on another element and pseudo class ::before

    how to apply div on hover my table td

    How to draw heart using HTML/CSS table?

    CSS help - how to keep my div in place with a responsive design

    How to stop breaking tables border when page is spliting?

    jQuery: How can I set css with own width?

    How to get smaller divs to stack to the right of larger divs?

    How to change CSS style of div after onclick method

    how to change the height of a div dynamically based on another div using css?

    How would you code this: The SO Menu [closed]

    How can I stop a text link to go up each time it is selected?

    How to transform a production to LL(1) for a list separated by a semicolon?

    How to combine 2 SASS functions as they do almost the same thing just using a different calculation?

    How to add CSS to an if else statement in PHP

    how to remove the last li::after for first line in specific width of an div

    How do I make a Google Map span 100% remaining height between header and footer?

    How to create a two vertically stacked buttons next to a text input

    gulp-cssmin: How to change relative URLs in minified CSS

    How to ease the whole css animation

    How do I put a box around my Flot graph?

    Can't figure out how to set color for specific div's in the loop

    How to make div box actually touching the side of the browser as mine has a gap?

    How to link to a CSS if a certain condition happens?

    How to join multiple rounded images one by one using line in css or css3

    CSS: How to set up border radius cross browser (only IE8 and IE9 missing ?)

    How can I disable inherit in CSS?

    How do I change the :-moz-placeholder color with jQuery once it's set?