Scaling Responsive Images in CSS

CSS @media make responsive images easy, but if you want your responsive images to scale between breakpoints things get a bit trickier.

It’s pretty easy to handle images responsively with CSS. Just use @media queries to swap images at various breakpoints in your design.

It’s slightly trickier to get those images to be fluid and scale in between breakpoints. Or rather, it’s not hard to get them to scale horizontally, but what about vertical scaling?

Imagine this scenario. You have a div with a paragraph inside it and you want to add a background using the :before pseudo element — just a decorative image behind some text. You can set the max-width to 100% to get the image to fluidly scale in width, but what about scaling the height?

That’s a bit trickier, or at least it tripped me up for a minute the other day. I started with this:

.wrapper--image:before {
    content: "";
    display: block;
    max-width: 100%;
    height: 443px;
    background-color: #f3f;
    background-image: url('bg.jpg');
    background-repeat: no-repeat;
    background-size: 100%;
 }

Do that and you’ll see… nothing. Okay, I expected that. Setting height to auto doesn’t work because the pseudo element has no real content, which means its default height is zero. Okay, how do I fix that?

You might try setting the height to the height of your background image. That works whenever the div is the size of, or larger than, the image. But the minute your image scales down at all you’ll have blank space at the bottom of your div, because the div has a fixed height with an image inside that’s shorter than that fixed height. Try re-sizing this demo to see what I’m talking about, make the window less than 800px and you’ll see the box no longer scales with the image.

To get around this we can borrow a trick from Thierry Koblentz’s technique for creating intrinsic ratios for video to create a box that maintains the ratio of our background image.

We’ll leave everything the way it is, but add one line:

.wrapper--image:before {
    content: "";
    display: block;
    max-width: 100%;
    background-color: #f3f;
    background-image: url('bg.jpg');
    background-repeat: no-repeat;
    background-size: 100%;
    padding-top: 55.375%;
}

We’ve added padding to the top of the element, which forces the element to have a height (at least visually). But where did I get that number? That’s the ratio of the dimensions of the background image. I simply divided the height of the image by the width of the image. In this case my image was 443px tall and 800px wide, which gives us 53.375%.

Here’s a working demo.

And there you have it, properly scaling CSS background images on :before or other “empty” elements, pseudo or otherwise.

The only real problem with this technique is that requires you to know the dimensions of your image ahead of time. That won’t be possible in every scenario, but if it is, this will work.