How to Use Responsive Background Image Sprites – CSS Tutorial

At one time or another, you may have found yourself wanting to use sprites in your responsive website design. While at first this may seem like a very simple task, in reality it’s a little more difficult of a challenge. It can, however, be done. Here’s how! This trick is fully supported by all modern browsers other than IE 8 and earlier, in which the image resizing aspect will not work properly, though it will not break completely.

Let’s start with our HTML. Let’s say we have part of a website here, and what we want is for the background image of this div to change when we hover over it. The catch is that we are using a sprite for it (a single image containing both our normal and rollover images) and it’s a responsive design. Let’s take a look.

<div class="sprite"></div>

Truly groundbreaking.

Okay, the html isn’t really that relevant I realize now. I could make it more fancy to try to make it a “real world example,” but why bother? Keep it simple, stupid.

The secret is clearly all in the css. Let’s say this div is always a square,  using our trick to maintain the same aspect ratio for an element in a responsive design. This might be the CSS of the element before we’ve added the background image.

div.sprite {
width: 20%;
height: 0;
padding-bottom: 20%;

Now we have a square image we want to nestle nicely inside there. And when we rollover the element, we want it to change. How do we do that? By adding the following css:

div.sprite {
width: 20%;
height: 0;
padding-bottom: 20%;
background-image: url("images/ourimage.png");
background-position: 0 0;
background-size: 200%;

Now here’s what’s really interesting about the background-size property. It does not STRETCH the image in both directions, it assumes that the image fits perfectly WIDTH-wise into our container, and then calls that 100%. If we want the image to be larger than what actually fits in the container, we go bigger. If we want it to be smaller and have to repeat, we go smaller. In our case, let’s say our image is 1000px wide and 500px tall. On the left 500px we have the first image, on the right 500px we have the rollover image. If we were to leave the setting at background-size: 100%;, we would end up with the image fitting completely within the div and repeating itself once underneath.

We don’t want this, however. We want the first half of the image only to fit. So we set background-size: 200%;, since we need it to be twice as large as the space we have assigned for it, since we only want the first half of the image to fit in our div.

Now it’s time to set everything up for the hover state. To do that, we add this to our css:

div.sprite:hover {
background-position: 100% 0; /* Use ONLY percents here */

The only thing we need to change is the background position! The secret is to place the background image using only percents. This is what allows it to be responsive. Actually USING this positioning system can be a little confusing, so let me break it down for you.

Calculating the background position

This is actually quite complicated and not at all intuitive. The best way I can explain it in words is this: the distance between 0% and 100% for your background position is the distance between when the left edge of your background image is touching the left edge of your element, and when the right edge of your background image is touching the right edge of your element.

So let’s break this down into a formula I came up with. This formula assumes each image in the sprite is the same size, and that each image is the same size as the element (i.e.: fits it exactly).

Percent Increments to Use = 100% / (Discrete images in your sprite – 1)

So if you have 4 images in your sprite: 100% / (4-1) = 100% / (3) = 33.33333%

So to display the first image, you set background-position: 0 0;. The second, background-position: 33.33333% 0;. And so on. Again, it’s not that intuitive, but with the formula it’s a little easier.

Let’s Consider another example. You have FIVE images in one sprite that you wish to use, all in a row. You want to display the SECOND image in the row. What background position do you use? Well… 100% / (5-1) = 25%. So if I wanted to display the second image, I would have to use background-position: 25% 0;. Easy!

If you would like to see a real-life and real-creepy example I made using my own face, check it out here!

About Brian Johnson

Brian Johnson is a website developer and designer living in Lakeville, Minnesota with a passion for code and WordPress. He spends his days building WordPress websites for small businesses, developing new code with the online community, and living life.

67 Comments on “How to Use Responsive Background Image Sprites – CSS Tutorial”

  1. Thanks, you saved my time! I was trying to convert size into em but your method is the easiest solution. However, I would suggest to keep at least couple of pixel white space around each frame because calculation sometimes give one pixel error and you may see little part of another frame. Adding extra 5-10 pixel space between sprite should not cause any problem.

    1. As per the formula in my page, you would move each image by an additional 100% / 86-1 = 1.17647%. The first image would be at 0, the second at 1.17647%, the third at 2.35294%, etc. Note, however, that this only applies if all of your sprites are the same size, and they are all in a row. If they aren’t all in the same row, then you are going to need to do these calculations but for both x and y based on which image they are in each row/column.

  2. very nice. thank you for this. I find the method used to calculate the exact background position great.

  3. This solution works fantastic in all browsers, but in IE and Edge every image is blurry and pixelated, forcing me to abandon the sprite and having separate individual fixed sized images for IE and Edge. My responsive design CSS is as follows:

    Here is a typical example:

    .sprite-24 {
    display: inline-block;
    width: 24px;
    height: 24px;

    .sprite-img.sprite-24.change_icon {
    background-position: 54.54% -24px;

    In this example, I am using 12 beautiful 512×512 image icons and using responsive design to display them at various sizes, 24×24 in this example. The images look gorgeous and even more beautiful when the browser is zoomed. But in IE and Edge the images are horrible. Those browsers simply don’t seem to support responsive design using the background-size property. Any thoughts would be much appreciated, and thanks again for the great tutorial.

    1. Nice to see Microsoft has learned exactly nothing from the failure of their old browsers… Honestly, I’m not sure how to avoid that! I imagine they think they are smarter than other browsers by rendering a smaller version of the image for some reason, but… Ya, I have no idea! How annoying.

  4. Pingback: How to create a responsive sprite sheet animation in css? - Tech Magazine

  5. I just used this after fiddling for hours with icon fonts and what not!!!! THANK YOU!!!!
    Just to add you have to change the background size to multiply by how many icons you have. So, I had 5 icons, the background size was 500%

  6. Pingback: responsive sprite background image, how to - DexPage

  7. Pingback: How to Make Any Website Responsive

  8. Hello. Nice Tutorial. But what is, if i don’t have only squares, what so often is the case, instead diferent sizes of images?

    1. You just adjust the width or padding as necessary. To make it twice as wide as it is tall, you might set it to 40% width and 20% bottom padding. Then just follow the formula to figure out how to use your background sprites.

  9. Pingback: [Responsible web] 반응형 웹에서 Image Sprite 사용하기 | Happy Javascript

  10. Great Tutorial, Thank you so much for this !!!

    just one question: can you further explain the logic behind choosing 33.3333 as a background-position when using 4 frames ?
    and how can we generalize this for n frames ?
    is there some kind of formula or it is about testing ?

    1. It’s a really confusing concept, but basically the percentage is simply the percentage of how far the image can travel before the other corner of the image is reached.

      This may sound strange, but let me explain it this way. If you had an element that was 100px wide, and a background image that was 100px wide, background-position would do literally nothing if you tried to use percentages. Because all 4 corners are already touching.

      Now let’s say you had an element 100px wide, and an image 101px wide. Background-position:100% 0; would only move the image a single pixel. Because the total distance it can move before the other end of it is lined up with the element is one pixel. Likewise, if you had an element 100px and an image 200px wide, then 100% would move it 100px. 50% would move it 50px.

      So let’s say we have a sprite with 4 different images on it. It’s 400px wide. And our element is 100px wide. First, let’s figure out what the total distance it can travel before the other end of the image is lined up with the edge of our element. 400-100 = 300px. So 100% = 300px.

      So if 100% is 300px, and we want to move our sprite 100px at a time, what percentage increments do we use at a time? 100/3 = 33%!

  11. Wow, didn’t think I’d be able to find a solution for this, but this worked perfectly. The zero height and bottom padding was the part I was missing, thanks a lot.

  12. Hi Brian ,
    Thanks, this helped me a lot. But one thing bothered me – somethies I need use max-width and min-width to my image.

    I found way to do this and I just want to share it with You:

    width: 30%;
    max-width: 400px;
    min-width: 200px;
    width: 100%;
    padding-bottom: 45%;
    background: URL(‘sprite.jpg’);
    background-size: 200%;
    background-position: 100% 0%;

    One thing to mention:
    padding-bottom of ‘sprite’ div is proportion of single image(not whole sprite) width to single image height, so:

    .sprite {
    padding-bottom: (image.width/image.height) %;

    Let me know what You think about it,

  13. HI Brian. Thanks for the tutorial. I’ve been looking at several sites for responsive sprites and yours was the easiest to follow. However, I can’t seem to get my head around the CSS needed for say, two different people side by side with alternate images beneath and hovering over would only change the image of the face you hover over. I made your sprite work vertically but not vertically and individually. I assume I would need to set up an unordered list to do it but haven’t been able to come up with the right CSS.

  14. Hello Brian, I am having trouble positioning my responsive sprite. I am sort of a beginner so it might be something really simple that I’m not recognizing. I want to maintain the same aspect ratio to the top and left of my browser window. Right now when my height is adjusted the sprite moves from my intended spot. I tried adding padding-top to the css but that did not work. Thanks for any help or advice you have.

  15. This is the first article that I’ve seen on responsive sprites. I thought it was possible, but now I know its possible. #GoodStuff

  16. Hi and thank you for the nice tutorial. There is not much for this topic when I google as I have been doing for a few days now. I think your tutorial comes up directly or is referenced in about a third of the search results. I have a question, though, if I may:

    I have plugged the code in and altered the shape a bit for my image and that works perfectly, but what I want to do with it is make a menu with a unique sprite for each item. I’ve made such in the past, but it was not responsive and, though I have tried to make this technique work by plugging it into ul/li properties, I have been failing miserably. Can this be made into a menu and, if so, what would the css look like?

  17. Brian, first off this is some great stuff! Very helpful post. The main issue that Im having with this is image wobble for cross-browser support. I feel confident that I’ve done the math correctly, but I assume that at different resolutions, different browsers are treating the % shifts by either rounding the pixel value up or down (creating the wobble). How have you dealt with this in your implementations?

    You can see an example here:
    It works wonderfully in Chrome (which handles sub-pixel values) at almost all resolutions, but seeing issues in Safari, some FF and IE9+. Any advice you might have would be appreciated!

    1. It looked like it was working fine to me! The browsers seemed to work differently in terms of how they resized things live during browser resizing, but seemed pretty consistent while the browser size was static.

  18. This is an awesome solution, thanks so much Brian! One item I am troubleshooting, when I use this sprite method my image is ever so slightly blurry. Any clue what this might be?

    1. Make sure the actual image size is the exact correct dimensions. If it’s even a pixel off, it will display blurry. I recommend inspecting the image element in your browser and verify what size it’s actually displaying at, and comparing that to the actual image file.

    2. I too had similar problem of images being blurry. My case was I had to target both retina display and normal devices using same image sprite file. This caused my image files to be double the dimension. I solved this issue by adding a 2px border on all images as per their background image on Photoshop (not using CSS). For me my background was white, so I added 2px white border on all images. And the blurry issue was gone.

  19. After an hour of playing with this conclude that it doesn’t work for me. Using a single large icon sprite (250×250) and the pixel shift on rollover is beyond acceptable parameters with IE and FF. Cannot test with your demo as the image changes entirely – might be useful to post a demo with a simple rollover icon color change so can test properly. But for now I’m back to square 1.

    1. at 40% the shift is in your face – at 36% it’s barely noticeable. Something to do with math I guess 😉

        1. If you are getting any kind of shift, odds are you are not doing the math correctly. You need to be exact. Depending on what you are attempting, it’s entirely possible you need to be getting down to the fraction of a percent. Again, the shift itself is hard to wrap your head around, you may be doing it incorrectly. Perhaps share your code on a js fiddle so I can see what you are dealing with and how to fix it.

  20. I’ve been looking for days for a solution to responsive sites with css sprites. Thank you for coming up with a great solution! SO helpful.

  21. Unrelated but interesting to note:
    I think using background-size: cover; works the same as 100% 200%; etc. it stretches the image till the div is filled and leaves out whatever is left (the other half of the sprite). It worked fine for my 16-part sprite if I’m not mistaken.

  22. I have the same problem as the previous 3 coments…on FireFox on Mac/Linux… a slight shift and it even shows a sub-pixel black or grey border on one or two sides of ‘some’ of the divs. I have a 16-part sprite with 8 responsive divs and rollover. FF is the only browser that remembers the sub-pixel amounts after the decimal (like .666%) in case the div relies on one to its left….it adds them together when needed. My divs are often 80.2444px wide by 40.2482px high and those extra bit get a shade applied to them somehow! Bummer. Might have to split the sprite into 8 sprites and try the simpler approach.

  23. This is a simple and effective implementation, yet it has the same problem as the other “responsive sprites” implementations I’ve encountered.

    The problem is non-chrome browsers will render a fraction of pixel from the next image. When you only have few images in a sprite and they both have same background this won’t be noticed, but when you have 20 images, the background will be wrong with a 20 times that fraction of a pixel, anywhere from 0 to 19.999px.

    I think the bug appears because most browsers round the sizes of elements to integers, but still use the raw number for percentage calculations. Chrome is the only one that works fine in this regard.

    I don’t know any fix for this, but I wish I did.

  24. Thanks for posting this article! It provided just the missing piece to a puzzle I’m currently working on. 🙂

    One question: during my testing, I’ve found in Firefox 25 (on Linux) one of the background changes causes a noticable “shift” in the background image. This doesn’t happen in Chrome 31, on the same system.

    My sprite is a JPEG image, 600×400 in dimension. I have four images in it, with each “normal” and “hover image in a row.

    The first row of images display and behave just fine in both Firefox and Chrome. The second row of images causes the “shift” behavior in Firefox that doesn’t appear in Chrome.

    Would you have any remote idea as to why this might be happening? The issue is a lot worse in Opera 12, but considering Opera 12 is pretty much dead, I won’t worry about it too much. At least not until I get the Firefox issue resolved. 🙂

    Thanks in advance!


  25. AWESOME! Everywhere else they said it couldn’t be done, or they had some javascript answer that may or may not work. This is an amazing solution! Thank You. My only other wish would be for Microsoft to give up on making browsers!

  26. Thanks for this great article.

    Everything up until the last paragraph was really helpful. But I had to do quite a bit of fiddling around until I understood exactly how images wider than 2 columns work. Unfortunately, I’m not entirely sure how to put it into words.

  27. hmmmmm I dont get it, I think I need to learn some part
    before this one. Thanks for Sharing, gonna bookmark this page
    so that I know were to find this.

  28. Wanted to check to see if you have heard of any Way to also support IE8. Would love to start using this but still have to support IE8.

    1. No problem! And I agree, it’s hard to find good resources about it. Glad I could help! Let me know if you have any other questions and I’ll do my best to answer.

  29. Pingback: How to Make CSS Image Sprites Responsive | Netmajic Social Media

Leave a Reply

Your email address will not be published. Required fields are marked *