CSS Only Method To Change IMG URL At Different @media screen Sizes

JeffTechnical Articles & Notes

I’ve been trying to keep Google PageSpeed Insights happy with my new websites at all possible screen resolutions, whilst simultaneously trying to make the sites look good on all devices, while at the same time relying on as little javascript or server-side processing as possible.

One problem I’ve had is that although it’s relatively easy to switch in a different image at different screen sizes (important so you don’t end up, for example, displaying an 800px wide image at 100px on a mobile device – a massively excessive download for the user, given the final render size of the image) by displaying images as backgrounds to block elements using CSS such as this,

.imagediv { width: 200px; height: 200px; background-image: url('images/my200x200_image.jpg'); }
@media screen and (min-width: 992px) {
.imagediv { width:100px; height:100px; background-image:url('images/someotherimage.jpg'); }
}

which works absolutely perfectly in many situations, it does not however work when you want the width of the image to vary smoothly as the screen width varies.

The problem comes from the fact that you can’t (that I knew of until now) automatically size a <div> to maintain its aspect ratio and so your image(s) will tend, as the screen resizes, to either be cropped, or squashed, one way or another.

There is a cunning trick however based on the little known fact that CSS property ‘padding-top’ if specified as a percentage is done so in relation to the width of the object it’s padding. Which means one can do this:

.myimagewrap {
  width: 100%;
  display: inline-block;
  position: relative;
}
.myimagewrap:after {
  padding-top: 46.0%;
  display: block;
  content: '';
}
.myimage {
  position: absolute;
  top: 0; bottom: 0; right: 0; left: 0;
  background: no-repeat center center;
  -moz-background-size:100% 100%;
  -webkit-background-size:100% 100%;
  background-size:100% 100%;
}

Which creates a <div> with width to match its container’s width, and a height in proportion to that width. In this case it’s 46% of that width.

You can then very easily define the different resolution images you’d like to use at various screen widths as follows:

@media screen and (min-width: 992px) {
  .myimage { background-image:url('images/mylargeimage.jpg'); }
}
@media screen and (min-width: 480px) {
  .myimage { background-image:url('images/mymediumimage.jpg'); }
}
@media screen and (min-width: 367px) {
  .myimage { background-image:url('images/mysmallimage.jpg'); }
}

In HTML it would then simply be:

&lt;div class="myimagewrap"&gt;
  &lt;div class="myimage"&gt;
  &lt;/div&gt;
&lt;/div&gt;

This is fabulous! However it’s replacing what was previously an <img> with a background image – which of course cannot normally be clickable. If you need it to be clickable too, I’ve found this works in all browsers I’ve tried. First define a new style:

.divlink {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  cursor: hand;
}

And then modify the HTML to:

&lt;div class="myimagewrap"&gt;
  &lt;div class="myimage"&gt;
    &lt;a href='https://www.aetherweb.co.uk' class='divlink'&gt;&lt;/a&gt;
  &lt;/div&gt;
&lt;/div&gt;

Brill!

I’ve used this exact technique to automatically scale and automatically use a lower resolution logo and header image for this site which I’m (at time of writing) currently in the process of converting from non-responsive to responsive:

http://www.whitbyonline.co.uk

The homepage was getting a PageSpeed score of 52/78 for mobile/desktop, it now gets 82/88.