CSS: Using Percentages in Background-Image

Last week you might have noticed we’re trialling a new experimental screen gadget in our books section — a JavaScript-powered book that allows you to thumb through the book to get a quick sense of the layout and feel.

While I might talk about that directly later, I thought I’d share some of my research on CSS background images along the way.

Like a lot of CSS/JS based animation these days, I used ‘background image sprites‘ — a large background-image containing all the frames, with background-position used to control which frame (or animation cell) is being displayed. Each click moves the background position exactly one frame down. The key advantage to using one image is you can guarantee all frames will be cached the moment your animation is ready.

There are three viable methods available to control background-position, and you are not allowed mix and match them (i.e. background-position: top 50%;).

1) Keywords : i.e. background-position: top right

Keywords are probably the most commonly used method, because they are really easy to get your head around and they work totally reliably on all browsers. You don’t even had to worry about their ordering — ‘top right’ works just as well as ‘right top’.

The main down side to keyword positioning is their lack of granularity. Three vertical (top, center, bottom) and three horizontal (left, center, right) gives you an absolute maximum of nine frame positions. There can be no ‘top centerish‘ or ‘right and a bit‘. Additionally, keywords can’t easily be manipulated mathematically like the other two numerical positioning methods.

A diagram explaining the use of pixels in background-image positioning2) Pixels : i.e. background-position: 0px 0px

Pixel positioning is currently probably the most useful method available. Firstly, the concept is easy to understand. Once we know we are always measuring from the top left corner of both the container and the image, it isn’t hard to predict exactly where a graphic will appear before it renders.

Pixels also lend themselves nicely to be manipulated by maths. In fact, if you have a looping animation (like the book animation), you can let your sprite graphic tile and simply move your background one ‘frame height’ each cycle. When the animation reaches the last frame, the tiling will automatically present the first frame and the loop automatically begins again.

3) Percentages : i.e. background-position: 80% 50%

Percentages are great in theory, but despite there being some very good explanations of it’s subtleties out there, I get the impression they (percentages) are not well understood. I certainly didn’t.

Percentages appear at first glance to have a lot of promise as they can be manipulated easily with maths and have no practical limits on the number of positions (i.e. frames) they can display. The idea that really got me interested in using them was their ability to allow you to resize your images without having to rewrite all your pixel positioning settings — 20% is 20% regardless of your image dimensions. Theoretically you should be able to give your script an image, tell it how many frames it has, and it will work out the rest.

A diagram explaining the use of percentages in background-image positioningIt all sounded great in theory, but some quick testing confounded me. While 0%, 50% and 100% gave me the equivalents of ‘top’, ‘center’ and ‘right’, percentages like 37%, 61% and 88% seemed to veer around crazily.

The key to understanding percentages in background images is understanding that unlike pixel settings, it’s reference point moves. Any percentage refers to a percentage position on the graphic AND on the containing object.

So, when we set a background image at ’10% 10%’, we are aligning a point on our graphic 10% across and down from it’s top left corner with a point on our HTML element 10% across and down from it’s top left corner.

I must admit this threw me at first but I drew this diagram to help explain it to myself.

This means in practice, if your graphic is the same size as your HTML element, changing the percentage will make no difference whatsoever. ’0%’ will be identical to ’73.5%’ and 100%.

Even when you understand the general concept it’s still not particularly intuitive. What happens if you give something a 150% X-position? It’s harder for most of us to imagine the result.

Four framesWhat about if you have a four frame vertical sprite? What would it’s four Y-positions be?

I started guessing that 25% and 75% must be in the mix, but, as you can see in this test case, the four vertical frame positions are set at thirds — 0%, 33.333%, 66.666% and 100%.

Ok,.. so it’s a little strange, but once you’ve got your head around it, it should be really useful, right?

IE 7 percentage bugIn theory, yes, but the reality is not so hopeful. Honestly, I’m so bored with whining about IE but this is really crappy.

As far as I can tell, both IE6 and IE7 (presumably earlier versions too) have some kind of rounding error which effectively kills using percentages with this type of sprite-based work. Simple percentages like 0%, 50% and 100% are fully reliable, but ultimately no more useful than top/center/bottom.

More complex percentages appear to be a raffle. The error is small, but more than enough to ruin an animation by forcing it off center — looped animations will multiply this error on each pass. Attempting to predict the error and compensate for it in IE also proved impossible, and I can’t imagine any other solution but to stick with pixel positioning

Suffice to say, very frustrating stuff. Insert ‘rolling my eyes emoticon’ here.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • http://diigital.com cranial-bore

    There are three viable methods available to control background-position, and you are not allowed mix and match them (i.e. background-position: top 50%;).

    It may be worth mentioning that percentage and lenth values can be combined, but not with keywords. eg. background-position: 50% 90px is acceptable.

    http://www.w3.org/TR/REC-CSS2/colors.html#background-properties

  • Vasilis

    Negative percentages in background-images give a very nice effect when resizing the window. You can see some examples on my site. Useless but nice.
    (excuses for the shameless selfpromotion, but I think this article is some sort of addition)

  • planewalker

    Great article, thanks ;) Answered a few questions I had on the topic that I had forgotten about.

  • Anonymous

    Instead of percentages and keywords, have you tried em’s?

  • http://www.sitepoint.com AlexW

    Instead of percentages and keywords, have you tried em’s?

    I didn’t, but I’m fairly sure they’d work fine. I’m just not sure how useful it is to position your background relative to your text size. Changing your text size would move your background, and off the top of my head, I can’t think of a good reason for wanting that to happen — but there may be one.

  • http://www.sitepoint.com AlexW

    (excuses for the shameless selfpromotion, but I think this article is some sort of addition)

    Agreed, Vasilis. Not sure how to get a lot of day to day use out of it, but it’s a nice effect.

  • Bill Rehm

    Couldn’t you just add an equal sized “blank” area at the end of your image? That way, the breaks would be 0-25-50-75.

  • http://www.sitepoint.com AlexW

    Yes, in simple terms, you could, Bill.

    It gets a little bit messy and less flexible, but it should work.

    The problems are you have to make all animations to a particular frame counts (i.e 3 frame animations are fine but all 4 frame animations become 5 frame, etc), then manually generate extra ‘padding frames’ when needed, and then have the script adjust so as not to display those blank frames when they exist.

    Not undoable — the script could predict if there will be a rounding error, assume you added the padding frame to the graphic, and adjust accordingly — but certainly losing some of it’s natural elegance.

  • Bill Rehm

    That’s true, Alex. But given the fact that these new blank background areas will result in a single continuous block of pixles of the same color, the impact on the size of the image would be minimal.

    Do you have code that generates the images now? If you do, it should be relatively straightforward to start with a blank image of the correct dimensions and “lay in” the real image tiles. If not, the same thing is easy enough to do on creation.

    The problem you identify seems to occur with the example you give without my suggestion, by the way. If you add another tile to your background image, you still need to readjust all of the percentages.

    As far as coding for it, if you’re going to write a method that generates the CSS classes for you, you could simply add a parameter to the method call that tells it how many blank frames you have. Assume zero if it’s undefined.

  • Poy

    I something i have been trying to figure out all day, if you know the answer can you please email me poyanp@gmail.com

    what i want to to is have sidebox_l_bg.gif (the drop shadow on the right side of the left column repeat-y but i would like it to start 16px below were is does now. Is that possible?? to have a point were the repeat-y begins

    #left_col{width:21%;text-align:left;vertical-align:top; background-position: 16px 0 0 0;background-image:#ffffff url(images/colum_top_bg.jpg) repeat-y;padding-top:16px;background:url(images/sidebox_l_bg.gif) repeat-y right; }
    chcek it out here
    http://www.spadirectory.com

  • Renato

    Nice article! But what’s the solution?

    I’m having the same problem here. IE7 and IE6 (as always) do not render it correctly… I dont have any idea on how to solve this problem. I’ve already tried using EMs instead of percentage, and it worked. But when the user resizes the window, it ruins the background again.. =( Very sad…

    Microsoft should review their own concepts and make an effort to correct all mistakes and give some attention to this, doing warned automatically updates!

  • http://www.sitepoint.com AlexW

    Renato, using pixels is the only foolproof method. You can probably (not fully tested) use percentages as long as the percentages you use are whole numbers. So three frame animations are fine (0%, 50%, 100).

    Four frame animations are no good (0%, 33.333%, 66.666%, 100%).

    Five frame should be ok (0%, 25%, 50%, 75%, 100%).

    Six frame no good, etc,..

  • serializer

    I’ve had reasonable success using multiples of -100%. This shifts the background by multiples of the width or height of the containing element. I’ve set up a basic example demonstrating a resizable graphical border using only 2 sprite images (one horizontal, one vertical) and very clean HTML+CSS. The bottom-right corner is a resize handle (using jQuery). I’m working this into a set of tutorials so it’s half-finished.

    There was a weird problem with the right and bottom borders; for some reason the effect of the background-position wrapped round at -100% so the background disappeared completely (unless repeat was specified for both axes but there are other reasons I didn’t want this!). I think it’s to do with the left:100% and top:100% positioning of those elements, in combination with the quirks of background positioning as described here. I worked around it by offsetting +100% instead which shifted onto the LAST sprite.

    I’ve tested on IE6+7, FF, Opera9, Safari3, all fine.

  • Anonymous

    There are three viable methods available to control background-position, and you are not allowed mix and match them (i.e. background-position: top 50%;).

    That was only true in css2 but css2.1 allows for keywords to be mixed and matched with percentages or pixels etc. It is prefectly valid to do this but some older browsers are still confused by it so its still best not to mix keywords into the mix. There really is no point anyway as the keywords equate exactly into eqivalent percentage sizes anyway.

  • mikeycampling

    Thank you for that clear explanation. The diagram gave me an instant penny-drop moment. I liked it so much I went out and joined the community.
    Thanks again.
    MC

  • http://www.sitepoint.com AlexW

    I remember a similar ‘penny drop’ moment myself, Mike. Glad you could get some good value out of the post.

  • Stevie D

    The way to think about percentage positioning is this.

    If you’ve got a containing block that’s 500px wide and an image that’s 300px wide, that leaves 200px of white space. That is what you are divvying up.

    If you set a value of 10%, that means 10% of the white space (=20px) will be to the left of the image (and 90% will be to the right), so the image is 20px in from the left. It’s a different calculation, but it gives you the same answer.

    If you are looking at multiple sprites, think of the hidden overflow instead of the white space.

    The answer to the IE rounding problem? I would guess that putting white/transparent space between each of the coloured blocks would solve the problem – the block might be a pixel out when using IE compared with Ffx/Opera, but you wouldn’t get that unsightly line in the wrong colour.

  • http://www.sitepoint.com AlexW

    If you’ve got a containing block that’s 500px wide and an image that’s 300px wide, that leaves 200px of white space. That is what you are divvying up.

    That’s a clever way to think about it, Stevie. I like that.

    The answer to the IE rounding problem? I would guess that putting white/transparent space between each of the coloured blocks would solve the problem – the block might be a pixel out when using IE compared with Ffx/Opera, but you wouldn’t get that unsightly line in the wrong colour.

    I think that would be ok if it caused a general flicker or jitter in the animation, but the problem is the one pixel mis-alignment that is generated on the first loop becomes 2, 3 and 4 on each new loop. The result is the whole animation begins to slide off the top of the frame and back onto the bottom.

    Unless you coded it to recalibrate on each pass (and you could) I can’t see it being viable.

  • Stevie D

    I think that would be ok if it caused a general flicker or jitter in the animation, but the problem is the one pixel mis-alignment that is generated on the first loop becomes 2, 3 and 4 on each new loop. The result is the whole animation begins to slide off the top of the frame and back onto the bottom.

    I don’t think it is a problem, as long as you make sure of the following points:
    - round decimals properly … so 33.33 -> 33% and 66.66 -> 67%
    - draw a 1px white/transparent border around the outside edge of each frame, so you will get a 2px white line between frames.

    That seems to be solving the problem for me, but if you’ve got a concrete example where it isn’t working, post it and I’ll see if I can fix it!

  • Scott Showalter

    I recently used negative percentages in my design for a Marine Electronics site. The site features an extensively designed underwater sea-life scene in the background which, when resized, moves the elements of the composition around in an interesting fasion. It’s a hidden gem, but a fun one for those curious to see how the design looks at a different page width.

    I was desperately trying to figure out the term for this sort of effect, but only thought of it after the fact. It’s called the “parallax effect” and I originally saw it on the Silverback site. But oddly, I only stumbled across that site again after the fact. Still cool to look at in hindsight though.

  • http://www.digitalgreenlight.com busy

    @Scott – Nicely done background! Little things like that can add a lot in terms of having a site that’s fresh, but you can’t quite put your finger on why (unless you’re a designer of course)

    @Alex – Excellent blog post. I had always just thought percentages were secretly laughing at me, now I see they actually make some sense. Bill Rehm beat me to the punch, mentioning how adding frames will solve the IE rounding problem.

    Personally I wouldn’t even worry about using blank frames to achieve this if you’re doing an animation, why not just use the right amount of frames from the beginning when you’re thinking it out? There may be cases where you absolutely need to have 4 and only 4, but is an extra useful frame going to cause problems in 90% of the situations?

    And if you’re just doing a hover image or something, then it won’t be so heavily scripted so a blank frame won’t add any extra complexity anyways.

  • kirankumar

    Thank you