Think of how many times you have coded a grid of images with an overlay that appears on hover and displays some sort of text — whether purely descriptive or a link to an article or page. I find myself having to do this for nearly every website I work on.

There was a pattern I used with background images that worked, but didn’t make good use of responsive images and wasn’t as semantic as it could be. Now, with the object-fit property getting wider support, there’s a better to achieve this.

Grid of images using CSS grid and figure element. Figcaption transitions in on hover. Linear gradient is used for a colorful background effect.
What we’ll build: a grid of article teasers with an image and text overlay on hover.

The Old Way

Here’s how I used to accomplish this:

<article class="blog-post-teaser">
    <div class="image" style="background-image: url(...)"></div>
    <div class="overlay">
        <h1><a href="...">Article Title</a></h1>
    </div>
</article>

The markup involved an article containing a plain div with a background image applied, and an overlay div that contained the text that needed to sit on top of the image. I would then absolutely position both the image and the overlay. The size of the div would be determined by a little percentage padding trick on the article:

.blog-post-teaser {
  position: relative;
  padding: 30% 0;
}

.blog-post-teaser .image {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.blog-post-teaser .overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

This approach gives us a lot of control but it’s really not as semantic as it could be. The image for each article seems like a pretty important piece of content, and the browser doesn’t know that it’s an image. And we can’t feed in alt tags to help with SEO.

How do we improve semantics while retaining control of the image sizing so everything fits nicely into a grid? Enter object-fit.

The New Way

Here’s what my markup looks like now:

<article class="blog-post-teaser">
    <figure>
        <img src="..." alt="...">
        <figcaption>
            <h1><a href="...">Article Title</a></h1>
        </figcaption>
    </figure>
</article>

Now the browser knows we’re serving up an image. It even knows that the article title is both a heading for the article and a caption for the image. Search engines can now access alt text. Let’s look at the styling:

.blog-post-teaser figure {
  position: relative;
  padding: 30% 0;
}

.blog-post-teaser figcaption {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.blog-post-teaser img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

It’s almost identical, only that now we’re using the figure element to control sizing, and we’re using the object-fit property on the image element. This works the exact same way as setting background-size: cover. The image will fill the specified space without stretching.

Browser Support

Currently object-fit is not supported in IE or Edge, but there is a simple polyfill you can use to convert the image to a background-image in those browsers.

Conclusion

Check out the live demo to see the styles in action, as well as some additional gradient and hover effects to make it more snazzy.