Crop Top

Avatar of Parker Bennett
Parker Bennett on (Updated on )

📣 Freelancers, Developers, and Part-Time Agency Owners: Kickstart Your Own Digital Agency with UACADEMY Launch by UGURUS 📣

The following is a guest post by Parker Bennett where he explores some different ways to approach the behavior of fluid and responsive images.

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-image

Edit 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
     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 Approach

Edit 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/[email protected]); 
      }
}

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 (Option One, Two, or Three), 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).