object-fit
and object-position
are my two favourite CSS properties lately. They give developers control over the content inside an img
or a video
similar to the way that we can manipulate the content of a background
with background-position
and background-size
.
object-fit
.
First, let’s dig into This property defines how an element, such as an img
, responds to the width and height of its content box. With object-fit
we can tell the content to fill that box in a variety of ways such as “preserve that aspect ratio!” or “stretch up and take up as much space as possible!”
Here’s an example:
This image is 400px x 260px. If we style that image like so…
img {
width: 200px;
height: 300px;
}
…then we end up with awkward distortion because that image is being squished to fit that container:
See the Pen 1b15cf30a6226d9f419b4f765458a059 by Robin Rendle (@robinrendle) on CodePen.
The content of our img
will take up all the available space inside its new box that we created when we changed its height and width, thus destroying its original aspect ratio.
To preserve the image’s aspect ratio whilst also filling in the space, we can use object-fit
:
.cover {
object-fit: cover;
}
See the Pen 7080df9cb7158c0860b8a66db9667a7d by Robin Rendle (@robinrendle) on CodePen.
The image on the left is our original and the image on the right has cut off the sides of the image, now preserving our aspect ratio! This might not look like a particularly interesting development at this scale but once we move into designing more realistic interfaces then the power of object-fit
reveals itself.
Let’s take another example:
See the Pen WrBzey by CSS-Tricks (@css-tricks) on CodePen.
Here we have two images and we want them to fill the width of 50% of the browser window (so they sit side by side) and 100% of the height. This we can do with the help of viewport units:
img {
height: 100vh;
width: 50vw;
}
The problem is that when we resize the browser we change the aspect ratio of the images which can cause all sorts of weirdness. Instead, we want to preserve their aspect ratio just like in the previous demo and we can use exactly the same method of doing so. object-fit: cover
to the rescue!
See the Pen YwbeoG by CSS-Tricks (@css-tricks) on CodePen.
Try resizing the browser again. No aspect-ratio weirdness, right? This is also extraordinarily helpful if we have images that have different dimensions because they’ll effectively be cropped by their bounding box.
cover
is only one of many values for object-fit
, more of which you can read about on the Almanac entry, but so far it’s the only value that I’ve found to be the most useful in day-to-day interface development.
object-position
.
Let’s move onto my next favourite thing: We’ll get things set up by using the same image as before and using these styles:
img {
background: yellow;
height: 180px;
object-fit: none;
}
Two things to note here: we need to declare the dimensions for our image in order for object-position
to work properly and we also need to set object-fit
to none
so that the image can be pushed around instead of filling up the whole box, as it would do by default. This makes a bit more sense when you see that the default for object-fit
on an image is fill
, even if you don’t declare it.
Speaking of defaults, object-position
centers all objects horizontally and vertically without a value:
img {
background: yellow;
height: 180px;
object-fit: none;
object-position: 50% 50%; /* even if we dont declare this the image will still be centered */
}
The first value moves the image left or right and the second moves it up or down. We can experiment with those values here:
See the Pen Object position by Robin Rendle (@robinrendle) on CodePen.
We can even nudge the image inside its content box so that we can reveal the background-color
that we set earlier.
But how is this useful? Good question! Well, in a recent project I found that a particular section of an image needed to be moved towards the center so that it caught the attention of the reader. We didn’t need to load a new image, and so we didn’t need the <picture>
element in this case, all we wanted to do was move the image over a little bit.
Besides moving the focus of an image, I’m not sure what else this property might be helpful for in a practical sense. But I’ve been messing around with object-position
to show how you can hide parts of an image and then reveal bits of it on click, like in this demo:
See the Pen SVG sprite with object-position by Robin Rendle (@robinrendle) on CodePen.
I haven’t experimented with how or why you might use this for <video>
elements. Full screen video that hits all the edges, perhaps? There’s plenty more to learn about and explore when it comes to these properties.
What’s the support?
Generally speaking, pretty good!
object-fit
is supported by everything except IE/Edge. Whereas, object-position
is supported by everything except Safari and IE/Edge.
I didn’t know this property even existed! It looks super useful, so I’m going to have to try it out some.
I’m also new to these properties, and very excited to try them out on my current project!
Nice. I published article about object-fit the other day that includes a easy fallback for IE (IE doesn’t support it at all).
https://pawelgrzybek.com/image-tag-vs-background-property/
I’ve found Firefox’s handling of object-fit to be a bit messed up, when used in combination with 2d/3d translation. See an example here where object-fit in Firefox causes an abrupt squishing, followed by a transition, followed by an un-squishing: http://codepen.io/davecranwell/pen/XXwbPQ
Anyone know a solution here?
The issue with the firefox wrapper 1 img is the
height: 100%;
declaration. If you replace it withheight: auto;
the problem is fixed. However, the position of the image changes from being centered to how the hacks are at the bottom.object-position use-case:
Zooming! Provide an overly-large image, which by default shrinks into the size you set. On hover, set “object-fit: none” and use JS to set object-position to a % corresponding to the mouse position within the image – instant zooming powers!
What a super cool idea! I haven’t seen that around anywhere, so I’ve made a quick demo of how it might work in practice:
http://s.codepen.io/robinrendle/debug/d65ccca115fdd4def0171019939635c3
It would be cool to animate from “object-fit: fill” to “object-fit: none” though so you see a nice zoom effect, although I can’t think of an easy way to work around that since property isnt animatable.
As usual… F**k IE
Agreed. I wouldn’t say that the browser support is ‘pretty good’ if IE doesn’t support it — much of the actual world uses IE, after all. If it were just IE8 that doesn’t support it, then I’d say yes, browser support is very good.
Personally, I’m dying to just build all my websites with the most modern CSS and JS, regardless of whether IE or Safari have deigned to support those features, but that doesn’t work for my clients. So, for now, for critical layout, I’ll have to stick with variations on Uncle Dave’s Ol’ Padded Box to fake
object-fit
.(As an aside,
object-fit: contain
is super useful for getting images of unknown dimensions to fit and be centred inside a box of known dimensions, like with tiled icons. This can sort of be done with Uncle Dave’s Padded Box, but only if you know the aspect ratio of the image.)Awesome, will definitely work with this.
It’s a cool feature, but not supported well enough for my standards. And the only polyfill I can find is deprecated… :(
Yeah, that sucks. I’ve taken to using a little bit of JS to convert
img.object-fit
into background images on the parent container, where you just usebackground-size
instead. It requires a little more configuration than just pasting in a polyfill, but it’ll do for now!The following works with
srcset
too:(a little untested)
I just finished developing a 1kb polyfill for
object-fit
on images bfred-it/object-fit-images that works well in IE9+ (possibly lower, but untested at the moment)The only real ‘flaw’ in
object-fit
is that there is no support for IE and Edge, and that’s not a minor flaw unfortunately. It’s a great property to use, but if your client uses one of those two browsers, even just to check his site, he’ll see messed up images.One can still just use
background
with url inside and then set thebackground-size
tocover
to ‘fit’ the image, but unfortunately you can’t reproduce theobject-fit
perfectly (the determining the size of your image to set the div to is kind of a nuisance).We’ll just have to hope that the MS guys will make this property work in the next releases of their Edge browser (since the IE will be discontinued anyhow).
Nice, once we have support it will simplify the fiddly technique that solves this right now:
.container {
position: relative;
overflow: hidden;
width: 400px;
height: 300px;
}
video {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%); // ie9
min-width: 100%;
min-height: 100%;
}
Using top: 0; and translateX(-50%), etc to shift positioning around.
For those mourning the loss of the object-fit polyfill…
A work around has been developed for object-fit:cover
https://medium.com/@primozcigler/neat-trick-for-css-object-fit-fallback-on-edge-and-other-browsers-afbc53bbb2c3#.igtqmicog
Chrome is very ordinary when it comes to correctly displaying image on this page. I opened the page on Chrome 47.0.2526.80, Firefox 44.0.2, and Internet Explorer 11 and only Firefox display the content correctly. Both Chrome and IE really stretched the image to format that didn’t look natural at all.
Robin, object-position would be useful when you want to scale the container’s width, height or both, but not the image. So, instead, the image is trimmed.
Sometimes scaling an image makes detail too small, so trimming is a better option..
Great article and a great css property. No more having to always use background-size/background-position. I stumbled upon these properties a few weeks ago while inspecting the muzli chrome extension. Great find.
will definitely look it up, but for the last codepen, i think there might be something wrong with it, the position, shouldn’t it be
0 0
,50% 0
and100% 0
, since there is no shift horizontally?