Cell with an image in CSS grid takes up too much space

Viewed 1314

I'm trying to create a 3 column layout where:

  • left and right colums are equal in width
  • left colum is empty
  • right column contains the image caption (a text of arbitrary length)
  • middle column contains an image. Image should have the height of 100% (fill the viewport vertically).
  • no layout shifts happen when the image loads (I included width and height attributes on the image).
  • I intend to use it with srcset and <picture> (too serve correct image sizes and jpeg/webp). I did not include this in the demo.

Here's my attempt:

body, figure {
  padding: 0;
  margin: 0;
}

figure {
  max-width: 100vw;
  max-height: 100vh;
  display: grid;
  grid-template-columns: 1fr min-content 1fr;
  grid-template-rows: 1fr;
  grid-gap: 1rem;
  
  border: 2px solid red;
}

figure::before {
  content: 'abc';
}

img {
  height: 100%;
  width: auto;
}
<figure>  
  <img src="http://placekitten.com/g/400/600" width="400" height="600" alt="cat">
  <figcaption>This is a cat</figcaption>
</figure>

This is how it looks like (screenshot shows grid tracks from Firefox dev tools):

unnecessary space around the image

The issue is this space I painted with red pen over. It makes the grid cell with the image too wide. I want this to look like this:

desired result - no space around the image

Here the grid cell is only as wide as it needs to in order to fit the image (preserving aspect ratio). As a result the image is centered horizontally.

How do I do that?

4 Answers

As a general rule, try to avoid making images into grid or flex items. There are still too many bugs in modern browsers that prevent reliable rendering.

I just wrote about this here: How can I get an image to fit to the size of a grid cell?

That said, here's a solution you may find useful:

body,
figure {
  padding: 0;
  margin: 0;
}

figure {
  display: grid;
  grid-template-columns: 1fr min-content 1fr;
  grid-gap: 1rem;
  height: 100vh;
  border: 2px solid red;
}

figure::before {
  content: 'abc';
}

div {
  min-height: 0;
}

img {
  object-fit: cover;
  height: 100%;
  vertical-align: bottom;
}

* {
  box-sizing: border-box;
}
<figure>
  <div>
    <img src="http://placekitten.com/g/400/600" width="400" height="600" alt="cat">
  </div>
  <figcaption>This is a cat</figcaption>
</figure>

Considering the fact that the image will be the highest element you can move the max-height:100vh to it and it will be easier:

body, figure {
  padding: 0;
  margin: 0;
}

figure {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  grid-template-rows: 1fr;
  grid-gap: 1rem;
  
  border: 2px solid red;
}

figure::before {
  content: 'abc';
}

img {
  max-height:calc(100vh - 4px); /* considering the border */
  width: auto;
  max-width:100%;
}
<figure>  
  <img src="http://placekitten.com/g/400/600" width="400" height="600" alt="cat">
  <figcaption>This is a cat</figcaption>
</figure>

try to put justify-content:center in the figure. and the max-width:100% in the img. this is what will center the image horizontally in the figure and makes it to take the full with.

A few changes I would make ;)

* {
  margin: 0;
  padding: 0;
}

html,
body {
  height: 100%;
  padding: 0;
  margin: 0;
}

body {
  min-height: 100%;
  padding: 0;
}

figure {
  max-width: 100vw;
  max-height: 100vh;
  display: grid;
  height: 100%;
  grid-template-columns: 1fr minmax(50%, min-content) 1fr;
  grid-gap: 1rem;
  border: 2px solid red;
}

figure::before {
  content: 'abc';
}

img {
  object-fit: contain;
  width: 100%;
  height: 100%;
}
<figure>
  <img src="http://placekitten.com/g/400/600" width="400" height="600" alt="cat">
  <figcaption>This is a cat</figcaption>
</figure>

Related