Bill
Bill

Reputation: 614

Adjust width and height of CSS grid boxes based on the number of images

I am creating a CSS grid that displays the photos of the guests in my podcast.

For my particular case, the grid will assume:

The basic structure of the HTML looks like this:

<div class="cards">
  <img src="/guests/guest1.png" alt="" class="card" class="card">
  <img src="/guests/guest2.png" alt="" class="card" class="card">
  <img src="/guests/guest3.png" alt="" class="card" class="card">
  <img src="/guests/guest4.png" alt="" class="card" class="card">
</div>

I want the guests to display within a CSS grid container.

enter image description hereNote: I do not want the images to look stretched, so I use object-fit: cover (but that is not the issue, I do not think)

.cards {
    display: grid;
    grid-template-columns: repeat(2, minmax(50%, 100%));
    gap: 2px;
    width: 110px;
    height: 110px;
  }

.card {
  width: 100%;
  object-fit: cover;
}

The rules I want to set are as follows:

  1. For 4 guests, display 4 boxes with 2 rows and 2 columns. This works!
  2. For 3 guests, display 3 boxes with 2 rows and 2 columns and do not stretch the guest across the entire second row. This works!
  3. For 2 guests, display 1 row and 2 columns at a 50% width for each image. This does not work :-(
  4. For 1 guest, display 1 row and 1 column at 100% width for the full image. This does not work :-(

As you can see in the screenshot, The grid does not display as I would hope for 2 guests or 1 guest.

Perhaps CSS Grid is the wrong choice or maybe I am missing something?

Ideally, I do not want to use JavaScript, but scss mixins would be okay (which may be a choice here?), but I would like to know if I can do this purely with CSS Grid or maybe Flexbox(?). If it helps any, I am using Jekyll with the Liquid language.

Is there a way I can create a CSS Grid that properly displays the photos so they are the correct size based on the number of images displayed?

Upvotes: 4

Views: 1789

Answers (3)

Tanner Dolby
Tanner Dolby

Reputation: 4421

Give the nested images inside your .cards grid a height: 100% declaration so they occupy 100% of each grid items height. To handle the scenario when there is only a single <img> element without any siblings inside the grid, you could use the :only-child pseudo class to target the .card which doesn't have any siblings. Then make it occupy both of the columns with grid-column: -1 / 1. This way the single image will span both columns occupying the original 110px width of your two column grid.

.cards {
  display: grid;
  grid-template-columns: repeat(2, minmax(50%, 1fr));
  gap: 2px;
  width: 110px;
  height: 110px;
  border: 2px solid #000;
  margin: 1rem 0;
}

.cards .card:only-child {
  grid-column: -1 / 1;
}

.card {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
<!-- Four cards -->
<div class="cards">
  <img src="https://images.unsplash.com/photo-1516125073169-9e3ecdee83e7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxleHBsb3JlLWZlZWR8NXx8fGVufDB8fHx8&auto=format&fit=crop&w=800&q=60" alt="cat" class="card" class="card">
  <img src="https://images.unsplash.com/photo-1516125073169-9e3ecdee83e7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxleHBsb3JlLWZlZWR8NXx8fGVufDB8fHx8&auto=format&fit=crop&w=800&q=60" alt="cat" class="card" class="card">
  <img src="https://images.unsplash.com/photo-1516125073169-9e3ecdee83e7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxleHBsb3JlLWZlZWR8NXx8fGVufDB8fHx8&auto=format&fit=crop&w=800&q=60" alt="cat" class="card" class="card">
  <img src="https://images.unsplash.com/photo-1516125073169-9e3ecdee83e7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxleHBsb3JlLWZlZWR8NXx8fGVufDB8fHx8&auto=format&fit=crop&w=800&q=60" alt="cat" class="card" class="card">
</div>

<!-- Three cards -->
<div class="cards">
  <img src="https://images.unsplash.com/photo-1516125073169-9e3ecdee83e7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxleHBsb3JlLWZlZWR8NXx8fGVufDB8fHx8&auto=format&fit=crop&w=800&q=60" alt="cat" class="card" class="card">
  <img src="https://images.unsplash.com/photo-1516125073169-9e3ecdee83e7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxleHBsb3JlLWZlZWR8NXx8fGVufDB8fHx8&auto=format&fit=crop&w=800&q=60" alt="cat" class="card" class="card">
  <img src="https://images.unsplash.com/photo-1516125073169-9e3ecdee83e7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxleHBsb3JlLWZlZWR8NXx8fGVufDB8fHx8&auto=format&fit=crop&w=800&q=60" alt="cat" class="card" class="card">
</div>

<!-- Two cards -->
<div class="cards">
  <img src="https://images.unsplash.com/photo-1516125073169-9e3ecdee83e7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxleHBsb3JlLWZlZWR8NXx8fGVufDB8fHx8&auto=format&fit=crop&w=800&q=60" alt="cat" class="card" class="card">
  <img src="https://images.unsplash.com/photo-1516125073169-9e3ecdee83e7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxleHBsb3JlLWZlZWR8NXx8fGVufDB8fHx8&auto=format&fit=crop&w=800&q=60" alt="cat" class="card" class="card">
</div>

<!-- One card -->
<div class="cards">
  <img src="https://images.unsplash.com/photo-1516125073169-9e3ecdee83e7?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxleHBsb3JlLWZlZWR8NXx8fGVufDB8fHx8&auto=format&fit=crop&w=800&q=60" alt="cat" class="card" class="card">
</div>

Upvotes: 2

UModeL
UModeL

Reputation: 1227

Nevertheless, it will be optimal for your task to use "display: flex". The blocks will themselves be arranged in the correct order. If the block is both the first and the last child (that is, the only one), then the block is stretched to fit the parent. I would do it like this:

.cards {
  display: flex;
  flex-flow: row wrap;
  gap: 4px; padding: 8px;
  height: 150px; width: 150px;
  box-shadow: -1px -2px 2px -1px #0004, inset 1px 2px 5px 0px #0004;
}

.card {
  width: calc((100% - 4px) / 2);
  height: calc((100% - 4px) / 2);
  object-fit: cover;
  box-shadow: 0 1px 3px #000;
}
.card:first-of-type:last-of-type {
  height: 100%; width: 100%;
}

body { margin: 0; min-height: 100vh; display: flex; justify-content: space-evenly; align-items: center; }
<div class="cards">
  <img src="https://i.pravatar.cc/?img=1" alt="1" class="card">
</div>
<div class="cards">
  <img src="https://i.pravatar.cc/?img=1" alt="1" class="card">
  <img src="https://i.pravatar.cc/?img=2" alt="2" class="card">
</div>
<div class="cards">
  <img src="https://i.pravatar.cc/?img=1" alt="1" class="card">
  <img src="https://i.pravatar.cc/?img=2" alt="2" class="card">
  <img src="https://i.pravatar.cc/?img=3" alt="3" class="card">
</div>
<div class="cards">
  <img src="https://i.pravatar.cc/?img=1" alt="1" class="card">
  <img src="https://i.pravatar.cc/?img=2" alt="2" class="card">
  <img src="https://i.pravatar.cc/?img=3" alt="3" class="card">
  <img src="https://i.pravatar.cc/?img=4" alt="4" class="card">
</div>

If you need two blocks to have 100% height, then add one more rule:

.cards {
  display: flex;
  flex-flow: row wrap;
  gap: 4px; padding: 8px;
  height: 150px; width: 150px;
  box-shadow: -1px -2px 2px -1px #0004, inset 1px 2px 5px 0px #0004;
}

.card {
  width: calc((100% - 4px) / 2);
  height: calc((100% - 4px) / 2);
  object-fit: cover;
  box-shadow: 0 1px 3px #000;
}
.card:first-of-type:last-of-type {
  height: 100%; width: 100%;
} 
.card:nth-last-of-type(2):first-of-type,
.card:nth-of-type(2):last-of-type {
  height: 100%;
}

body { margin: 0; min-height: 100vh; display: flex; justify-content: space-evenly; align-items: center; }
<div class="cards">
  <img src="https://i.pravatar.cc/?img=1" alt="1" class="card">
</div>
<div class="cards">
  <img src="https://i.pravatar.cc/?img=1" alt="1" class="card">
  <img src="https://i.pravatar.cc/?img=2" alt="2" class="card">
</div>
<div class="cards">
  <img src="https://i.pravatar.cc/?img=1" alt="1" class="card">
  <img src="https://i.pravatar.cc/?img=2" alt="2" class="card">
  <img src="https://i.pravatar.cc/?img=3" alt="3" class="card">
</div>
<div class="cards">
  <img src="https://i.pravatar.cc/?img=1" alt="1" class="card">
  <img src="https://i.pravatar.cc/?img=2" alt="2" class="card">
  <img src="https://i.pravatar.cc/?img=3" alt="3" class="card">
  <img src="https://i.pravatar.cc/?img=4" alt="4" class="card">
</div>

Upvotes: 0

Lakshan Costa
Lakshan Costa

Reputation: 643

This should work for what you are asking for, You can add or remove the img src to get the different results.

html

<div class="cards">
  <img src="https://www.w3schools.com/images/lamp.jpg" alt="" class="card" class="card">
  <img src="https://www.w3schools.com/images/lamp.jpg" alt="" class="card" class="card">
  <img src="https://www.w3schools.com/images/lamp.jpg" alt="" class="card" class="card">           
</div>

Css

.cards {
    display: grid;
    grid-template-columns: repeat(2, minmax(50%, 100%));
    gap: 2px;
    width: 300px;
    height: 110px;
  }

.card {
  width: 100%;
  object-fit: cover;
}

/* one item */
img:first-child:nth-last-child(1) {
/* -or- li:only-child { */
  width: 300px;
  height: 300px
}

/* two items */
img:first-child:nth-last-child(2),
img:first-child:nth-last-child(2) ~ img {
  width: 150px;
  height: 300px;
}

/* three items */
img:first-child:nth-last-child(3),
img:first-child:nth-last-child(3) ~ img {
    
}

/* four items */
img:first-child:nth-last-child(4),
img:first-child:nth-last-child(4) ~ img {
    
}

You can change the nth element in the way you want them to display. If you change the width and height to % it will take the % of the image. That's why I added the width and height in pixels according to the grid size. I'm not sure how to use the grid width and height as a % there. (There's probably a way to do that too. I'll add it if I find a way).

But everything else should work.

Upvotes: 1

Related Questions