CSS Grid with children with set aspect ratio fit to parent

Viewed 733

I need a grid with fixed amount of columns and rows. Each column and row are filled in completely. Each cell consists of two parts - image with fixed aspect ratio (same for all cells) and caption with fixed height. I need grid to fit to parent (analogous how background-size: contain works). So far I came up with solution, which only fits grid to match width of parent.

Note: in this example parent of grid is whole screen, in real case it can be any size independent of screen size (so we cannot use vw and vh units).

html, body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
}
.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(2, 1fr);
  padding: 15px;
  gap: 15px;
}
.grid-child {
  background-color: red;
}
.fixed-ratio {
  aspect-ratio: 4/3;
  background-color: orange;
}
.caption {
  background-color: yellow;
}
<div class="grid">
  <div class="grid-child"><div class="fixed-ratio"></div><div class="caption">1</div></div>
  <div class="grid-child"><div class="fixed-ratio"></div><div class="caption">2</div></div>
  <div class="grid-child"><div class="fixed-ratio"></div><div class="caption">3</div></div>
  <div class="grid-child"><div class="fixed-ratio"></div><div class="caption">4</div></div>
  <div class="grid-child"><div class="fixed-ratio"></div><div class="caption">5</div></div>
  <div class="grid-child"><div class="fixed-ratio"></div><div class="caption">6</div></div>
</div>

2 Answers

Unless I completely misunderstand what you are wanting to achieve simply adding height 100% to .grid will make the grid fill the screen.

Reszing the screen will allow the content of the cell (image) to scale to the width of the parent while maintaining aspect ratio.

NOTE

The grid child image cannot fill the height of the cell while maintaining an aspect ratio of 4/3 unless the width of the container/screen is also an aspect ratio of 4/3.

The grid child text cannot fill the cell because it has a fixed height.

html, body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
}
.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(2, 1fr);  
  gap: 15px;
  height:100%;
}
.grid-child {
  background-color: red;
}
.fixed-ratio {
  aspect-ratio: 4/3;
  background-color: orange;
}
.caption {
  background-color: yellow;
}
<div class="grid">
  <div class="grid-child"><div class="fixed-ratio"></div><div class="caption">1</div></div>
  <div class="grid-child"><div class="fixed-ratio"></div><div class="caption">2</div></div>
  <div class="grid-child"><div class="fixed-ratio"></div><div class="caption">3</div></div>
  <div class="grid-child"><div class="fixed-ratio"></div><div class="caption">4</div></div>
  <div class="grid-child"><div class="fixed-ratio"></div><div class="caption">5</div></div>
  <div class="grid-child"><div class="fixed-ratio"></div><div class="caption">6</div></div>
</div>

I managed to find one solution using a bit of Javascript and ugly usage of CSS calc function. I welcome another solutions which give same result but do not use JS. You can use this snippet to compare results of your own (please open snippet in full screen and resize window to see it in action).

document.addEventListener('DOMContentLoaded', () => {
  new ResizeObserver(entries => {
    for (let entry of entries) {
      entry.target.style.setProperty('--parent-width', entry.contentRect.width + 'px');
      entry.target.style.setProperty('--parent-height', entry.contentRect.height + 'px');
    }
  }).observe(document.body);
});
  * {
    box-sizing: border-box;
  }
  html {
    --columns: 3;
    --rows: 2;
    --ratio: 4 / 3;
    --gap: 15px;
    --caption-height: 20px;
  }
  html, body {
    margin: 0;
    padding: 0;
    width: 100%;
    height: 100%;
  }
  .grid {
    background-color: red;
    padding: calc(var(--gap) / 2);
    width: calc(
      min(
        var(--parent-width),
        (((var(--parent-height) - var(--gap)) / var(--rows) - var(--caption-height) - var(--gap)) * (var(--ratio)) + var(--gap)) * var(--columns) + var(--gap)
      )
    );
    height: calc(
      min(
        var(--parent-height),
        (((var(--parent-width) - var(--gap)) / var(--columns) - var(--gap)) / (var(--ratio)) + var(--caption-height) + var(--gap)) * var(--rows) + var(--gap)
      )
    );
    margin: auto;
  }
  .grid-child {
    padding: calc(var(--gap) / 2);
    width: calc(100% / var(--columns));
    height: calc(100% / var(--rows));
    float: left;
  }
  .fixed-ratio {
    background-color: orange;
    height: calc(100% - var(--caption-height));
  }
  .caption {
    background-color: yellow;
    height: var(--caption-height);
}
<div class="grid">
  <div class="grid-child"><div class="fixed-ratio"></div><div class="caption">1</div></div>
  <div class="grid-child"><div class="fixed-ratio"></div><div class="caption">2</div></div>
  <div class="grid-child"><div class="fixed-ratio"></div><div class="caption">3</div></div>
  <div class="grid-child"><div class="fixed-ratio"></div><div class="caption">4</div></div>
  <div class="grid-child"><div class="fixed-ratio"></div><div class="caption">5</div></div>
  <div class="grid-child"><div class="fixed-ratio"></div><div class="caption">6</div></div>
</div>

Related