Sometimes you want an image to resize responsively but restrict its height — cropping it then as it widens. Here, we explore three options with various trade-offs.
background-position: center bottom.Here, in place of an img, we create a div with a background-image and use CSS3's dandy new background-size: cover to have it size proportionally. As a bonus, we can easily crop from the top, center, or bottom using background-position.
One potential issue is we need to specify a height for the div to show up. This gives it a fixed height that doesn't scale proportionally (at least not without CSS media queries). When it gets narrow enough, the sides start cropping (this might be preferable, it depends).
Also, a quick check with caniuse.com shows this will fail conspicuously in IE8 (showing the image full size), but thanks to Louis-Rémi Babé, there's a background-size polyfill workaround (though it does require a relative or fixed position, and a z-index). IE8 also needs a polyfill for CSS media queries. There are a couple options.
.bg-image {
/* image specified in separate class below */
height: 240px;
width: 100%; }
.bg-image-wedding {
background-image: url(img/photo-wedding_1200x800.jpg);
/* lt ie8 */
-ms-background-position-x: center;
-ms-background-position-y: bottom;
background-position: center bottom;
/* scale bg image proportionately */
background-size: cover;
/* ie8 workaround -
http://louisremi.github.io/background-size-polyfill/ */
-ms-behavior: url(/backgroundsize.min.htc);
/* prevent scaling past src width (or not) */
/* max-width: 1200px; */ }
/* example media queries */
@media
only screen and (min-width : 768px) {
.bg-image { height: 320px; }
}
@media
only screen and (min-width : 1200px) {
.bg-image { height: 400px; }
}
background-position: center center.Something to note, background-size: cover will readily upscale larger than the src image's native size (or not, if you set a max-width).*
Still, there are some downsides to using a background-image. It's not as semantic or modular as an img, so it's less straightforward to maintain. You're seemingly stuck with a fixed height or cumbersome CSS media queries. Also, users can't save the image as easily (sometimes preferable).
Here, we use an img with max-width set to a percentage of the containing element so it scales responsively, then wrap it in a div with overflow: hidden and a specified height or max-height.
.crop-height {
/* max-width: 1200px; /* img src width (if known) */
max-height: 320px;
overflow: hidden; }
img.scale {
/* corrects inline gap in enclosing div */
display: block;
max-width: 100%;
/* just in case, to force correct aspect ratio */
height: auto !important;
width: auto\9; /* ie8+9 */
/* lt ie8 */
-ms-interpolation-mode: bicubic; }
As you can see, the bottom of the image now gets cropped as it widens. But what if you want it to crop from the top? Surprisingly, you can — using CSS3's transform:rotate() we add a "flip" class to both img.scale and div.crop-height — flipping the img all the way around.
/* apply to both img.scale and div.crop-height */ .flip { -webkit-transform: rotate(180deg); -moz-transform: rotate(180deg); -ms-transform: rotate(180deg); -o-transform: rotate(180deg); transform: rotate(180deg); /* needed? not sure */ zoom: 1; } img.flip { /* if native or declared width of img.scale is less than div.crop-height, this will align flipped img left */ float: right; /* add clearfix if needed */ }
Novel, but limited to top-cropping only. And, as you might expect, this doesn't work in IE8, either — perhaps failing less conspicuously (it will just crop from the bottom as before). The bad news is, I haven't found any polyfill options that work because they need a specified height and width.
What if we could have the advantages of specifying an img (e.g., using max-height so we get proportional vertical scaling below a certain height), but also the flexible cropping and IE8 polyfill support afforded a background-image? We can!
visibility: hidden, the background-image has background-position: center center.The trick is to make the responsively-sizing img invisible. With visibility: hidden it retains layout, so we see the background-image behind it. (Since it's the same image source it shouldn't have to download twice.) If you want user-friendly access, you could instead use opacity: 0. Now users can drag the image or right-click to save. (Opacity and background-size need some extra IE8 workaround bits.)
opacity: 0. A background-image you can drag or right-click to save. Works in IE8 (with help).Another thought is to use a more compressed proxy img, and use CSS media queries to serve a higher-resolution background-image as needed. This img could even be a proportionately smaller size (e.g., matching your max-height), or watermarked. Check out this CSS-Tricks post on media queries, and this helpful resolution mixin for SASS.
img is holding the space for the higher-res background-image.
.crop-height {
/* max-width: 1200px; /* img src width (if known) */
max-height: 320px;
overflow: hidden; }
.bg-image-wedding {
/* for small devices */
background-image: url(img/photo-wedding_1200x800.jpg);
/* lt ie8 */
-ms-background-position-x: center;
-ms-background-position-y: bottom;
background-position: center bottom;
/* scale bg image proportionately */
background-size: cover;
/* ie8 workaround - http://louisremi.github.io/background-size-polyfill/ */
-ms-behavior: url(/backgroundsize.min.htc);
*/ prevent scaling past src width (or not) */
/* max-width: 1200px; */ }
.invisible {
visibility: hidden; }
.transparent {
/* trigger hasLayout for IE filters below */
zoom: 1;
/* 0 opacity in filters still displays layout */
-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
filter: alpha(opacity=10);
opacity: 0; }
/* example media query for smaller non-retina devices */
@media
only screen and (max-device-width : 600px) and (-webkit-max-device-pixel-ratio: 1),
only screen and (max-device-width : 600px) and ( max-device-pixel-ratio: 1) {
.bg-image-wedding {
background-image: url(img/photo-wedding_600x400.jpg); }
}
/* example media query for retina ipad and up */
@media
only screen and (min-device-width : 768px) and (-webkit-min-device-pixel-ratio: 1.5),
only screen and (min-device-width : 768px) and ( min-device-pixel-ratio: 1.5) {
.bg-image-wedding {
background-image: url(img/photo-wedding_1200x800@1.5x.jpg); }
}
Until the day where CSS3's object-fit is supported, which may be a while, this hybrid option seems like the best approach to me. Still, it's nice to have options. I hope you got something out of my way-too-thorough exploration. You can take a look at the source code for more, edit on CodePen, or download the example files here. If you have any questions, comments, or corrections, drop me a line: parker@parkerbennett.com.
<img width="2400px" height="1600px" src="img/photo-wedding_1200x800.jpg /> (edit on CodePen).