Search code examples
htmlcsscss-gridresponsive

css grid equal height for each element keeping responsivness


I have the grid, each element has an image and other divs underneath. Because it's a grid, each element has responsive width and so it scales automatically as the screen shrinks. I would like the images (which can have different sizes and not be homogeneous) of the elements to have the same height while maintaining this responsive feature. If I set a specific height, as the screen shrinks the aspect ratio is not maintained. Basically I need a youtube style responsivness, that is a perfect example. How can I do it?

This is my grid:

JSX:

<div className="home-container">
  {elements.map((el) => (
    <Element el={el}></Element >
  ))}
</div>

CSS:

.home-container {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 20px;
  margin-left: 250px;
  grid-auto-rows: 1fr;
  grid-row-gap: 30px;
}

This is my elementcard:

JSX:

<div className="element-container">
   <div className="element-img">
      <img src={imageUrl} alt={title} />
   </div>
   <div>...</div>
   <div>...</div>
</div>

CSS:

.element-container {
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  height: fit-content;
  border-radius: 10px;
  overflow: hidden;
}

.element-img {
  display: flex;
  margin-bottom: 20px;
}

.element-img > img {
  width: 100%;
  height: 100%;
  border-radius: 20px;
  object-fit: cover;
}

PROBLEM: As you can see, the dish problem is ok when the grid is not shrunk, but then that it does not maintain the same aspect-ratio as the other elements.

enter image description here


Solution

  • Concept

    1. Create a wrapper around the image to maintain the aspect ratio. Use position: relative; and set a padding-top to define the aspect ratio.

    2. For the image, set position:absolute; to position it within the wrapper. Use object-fit: cover; to maintain the aspect ratio while covering the entire container. Set width:100%; and height:100%; to fill the container.


    Solution

    .home-container {
      display: grid;
      grid-template-columns: repeat(4, minmax(0, 1fr));
      gap: 20px;
      margin-left: 250px;
      grid-auto-rows: 1fr;
      grid-row-gap: 30px;
    }
    
    .element-container {
      display: flex;
      flex-direction: column;
      box-sizing: border-box;
      min-height: 250px;
      border-radius: 10px;
      overflow: hidden;
    }
    
    .element-img {
      position: relative;
      width: 100%;
      padding-top: 56.25%; /* 16:9 */
      margin-bottom: 20px;
    }
    
    .element-img > img {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      object-fit: cover;
      border-radius: 20px;
    }
    

    The padding-top in .element-img is what maintains the aspect ratio. When you set the padding-top you're essentially creating an invisible box with the specified aspect ratio. The padding-top is calculated as a percentage of the width of the element, so it maintains the aspect ratio regardless of the actual width of the element, which is what makes it responsive.


    Aspect Ratios

    • Widescreen: 16:9 = 56.25%
    • Standard: 4:3 = 75%
    • Square: 1:1 = 100%
    • Photography: 3:2 = 66.66%
    • Ultrawide: 21:9 = 42.85%

    You can calculate the aspect ratio using the following formula:

    Padding Percentage = (Height / Width) * 100

    16:9 Example:

    56.25 = (9/16) * 100

    Or you can just use any percentage you think works the best :)


    Example

    .grid-container {
      display: grid;
      grid-template-columns: repeat(4, 1fr);
      gap: 20px;
      padding: 20px;
    }
    
    .grid-item {
      background-color: #f0f0f0;
      border: 1px solid #ddd;
      padding: 10px;
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    }
    
    .aspect-ratio-16-9 {
      position: relative;
      width: 100%;
      padding-top: 56.25%;
    }
    
    .aspect-ratio-16-9 img {
      position: absolute;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
    <div class="grid-container">
      <div class="grid-item">
        <div class="aspect-ratio-16-9">
          <img src="https://via.placeholder.com/300x169" alt="300x169 Image">
        </div>
      </div>
      <div class="grid-item">
        <div class="aspect-ratio-16-9">
          <img src="https://via.placeholder.com/400x225" alt="400x225 Image">
        </div>
      </div>
      <div class="grid-item">
        <div class="aspect-ratio-16-9">
          <img src="https://via.placeholder.com/500x281" alt="500x281 Image">
        </div>
      </div>
      <div class="grid-item">
        <div class="aspect-ratio-16-9">
          <img src="https://via.placeholder.com/600x338" alt="600x338 Image">
        </div>
      </div>
      <div class="grid-item">
        <div class="aspect-ratio-16-9">
          <img src="https://via.placeholder.com/700x394" alt="700x394 Image">
        </div>
      </div>
      <div class="grid-item">
        <div class="aspect-ratio-16-9">
          <img src="https://via.placeholder.com/800x450" alt="800x450 Image">
        </div>
      </div>
      <div class="grid-item">
        <div class="aspect-ratio-16-9">
          <img src="https://via.placeholder.com/900x506" alt="900x506 Image">
        </div>
      </div>
      <div class="grid-item">
        <div class="aspect-ratio-16-9">
          <img src="https://via.placeholder.com/1000x563" alt="1000x563 Image">
        </div>
      </div>
    </div>


    Resources

    Below I've included some related resources that might be useful to future users for understanding why and how this works, and to reduce issues with implementation and adjustments.