Crop Top

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.

The happy couple, front and center: background-position: center bottom.

Option One: background-imageEdit on CodePen

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; }
    }
That's me in the middle: 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).

Option Two: img with a Twist Edit on CodePen

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.

wedding
When you resize past 700px, the bride disappears.
.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.

wedding
There's the happy couple! (Unless you're using IE8 or lower.)
/* 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.

Option Three: Hybrid ApproachEdit on CodePen

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!

The img has 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.)

wedding
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.

wedding
Here, a 70 KB half-scale 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); }
    }

Wrapping Up

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.


*You can make an img upscale like a background-image if you want. You just need to "pre-enlarge" it, adding a proportionately larger width and height to the img element itself: <img width="2400px" height="1600px" src="img/photo-wedding_1200x800.jpg /> (edit on CodePen).