Centre CSS Grid Items Dependent On Dynamic Content Count
Asked Answered
L

3

7

I have a series of images that are fetched from a database, and when three or more images are added it visually shows the three columns.

When less than three images are present, because I'm using display: grid; it is currently justified to the left of the parent container (in the code example I've just used red boxes to represent the images).

Is there anyway of having it so that when one or two images are present these are justified to the centre of the parent element. I appreciate I could use javascript to detect how many images are present and if it is less than three, add a class and change the wrapper to display: flex, but I wondered if such a layout was possible with CSS only?

Due to the nature of the layout I do need to use CSS Grid when more than three images are present.

Note: I've commented out two of the red boxes in the HTML to show the initial issue when only one red box is present.

Codepen: https://codepen.io/anna_paul/pen/xxXrVJQ

body {
  display: flex;
  justify-content: center;
  margin: 0
  width: 100%;
  height: 100vh;
}

.wrapper {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  grid-gap: 1rem;
  max-width: 1250px;
}

.box {
  width: 200px;
  height: 200px;
  background: red;
}
<div class="wrapper">
  <div class="box"></div>
<!--   <div class="box"></div>
  <div class="box"></div> -->
</div>
Laudable answered 19/12, 2021 at 3:8 Comment(4)
Could you explain why due to the nature of your layout it has to be grid not flex box? Does the most recent answer on #46277293 help?Hedgepeth
@AHaworth because of the nature of the image grid, and how much easier it is to do responsively with grid instead of flex.Laudable
@paulo_cam so when 1 or 2 images are there you need them to be centered. And when 3 or more are there you want them left aligned? Can you show required outputs for each use case in OP? and add border to container so we'll understand free space in each case.Moran
try this .wrapper { display: grid; grid-template-columns: auto 1fr; grid-template-rows: auto 1fr auto; grid-gap: 1rem; max-width: 1250px; justify-content: center; }Ayeaye
E
8

Do it like below:

.wrapper {
  display: grid;
  grid-auto-flow:column; /* column flow */
  justify-content:center; /* center everything */
  grid-gap: 1rem;
  max-width: 600px;
  border:1px solid;
  margin:10px auto;
}
/* make sure you only have 3 columns*/
.box:nth-child(3n + 1) {grid-column:1}
.box:nth-child(3n + 2) {grid-column:2}
.box:nth-child(3n + 3) {grid-column:3}
/**/
.box {
  width: 100px;
  height: 100px;
  background: red;
}
<div class="wrapper">
  <div class="box"></div>
</div>
<div class="wrapper">
  <div class="box"></div>
  <div class="box"></div>
</div>
<div class="wrapper">
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
</div>
<div class="wrapper">
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
</div>
<div class="wrapper">
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
</div>
<div class="wrapper">
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
  <div class="box"></div>
</div>
Excreta answered 23/12, 2021 at 22:21 Comment(2)
+1, this is the right answer but careful is you copy and paste,max-width: 600px; is incorrect for a square of 200px. Since we can have two gap of 1rem you need to set max-width: calc(600px + 2rem);.Veriee
@BenSouchet what copy/past? 600px is a random big value used for the sake of the demo. It's irrelevant and can be removed btw. It has no relation with the boxes width.Excreta
M
0

Because you are using 1fr for each column, even if a column has no content it is taking 33% of the free space. You need to specify units other than fr(fraction of the available space) unit:

grid-template-columns: auto auto auto;
grid-template-columns: repeat(3, auto);

grid-template-columns: repeat(3, minmax(min-content, max-content));

Use any of the above. There is a small difference between auto and minmax(min-content, max-content).

Following is the demo with 3 containers with 1, 2 and >3 items respectively:

body {
  margin: 0;
  width: 100%;
  height: 100vh;
}

.demo {
  display: flex;
  justify-content: center;
}

.wrapper {
  display: grid;
  grid-template-columns: repeat(3, minmax(min-content, max-content));
  
  grid-gap: 1rem;
  width: auto;
  max-width: 1250px;
}

.box {
  width: 200px;
  height: 200px;
  background: red;
  border: 1px solid;
}
<p>Grid with one item</p>
<div class="demo">
  <div class="wrapper">
    <div class="box">1</div>
  </div>
</div>
<hr>
<p>Grid with two items</p>
<div class="demo">
  <div class="wrapper">
    <div class="box">1</div>
    <div class="box">2</div>
  </div>
</div>
<hr>
<p>Grid with >3 items</p>
<div class="demo">
  <div class="wrapper">
    <div class="box">1</div>
    <div class="box">2</div>
    <div class="box">3</div>
    <div class="box">4</div>
  </div>
</div>

The 1rem grid-gap will remain even if the column has 0 width. So in your setup, grid inside flex, the item in one item grid will be off by 2rem(2grid-gaps) from the center. If this is not a big deal then no worries.
But if you want exact center then you need to make grid-gap:0. And use spacing in side grid items(.box) like margin: 0.5rem; or padding:0.5rem to make artificial grid-gap.

body {
  margin: 0;
  width: 100%;
  height: 100vh;
}

.demo {
  display: flex;
  justify-content: center;
}

.wrapper {
  display: grid;
  grid-template-columns: repeat(3, minmax(min-content, max-content));
  
  width: auto;
  max-width: 1250px;
}

.box {
  width: 200px;
  height: 200px;
  background: red;
  border: 1px solid;
  
  margin: 0.5rem;
}
<p>Grid with one item</p>
<div class="demo">
  <div class="wrapper">
    <div class="box">1</div>
  </div>
</div>
<hr>
<p>Grid with two items</p>
<div class="demo">
  <div class="wrapper">
    <div class="box">1</div>
    <div class="box">2</div>
  </div>
</div>
<hr>
<p>Grid with >3 items</p>
<div class="demo">
  <div class="wrapper">
    <div class="box">1</div>
    <div class="box">2</div>
    <div class="box">3</div>
    <div class="box">4</div>
  </div>
</div>
Moran answered 23/12, 2021 at 5:56 Comment(2)
Thanks Onkar. In the second example with two boxes, the grid isn't centred - if you click on it with the dev tools it's offset by 1rem caused by the grip gap property. Is there anyway of preventing that?Laudable
I've explained that in the answer. Added second code snippet demonstrating the solution.Moran
C
-2

using auto instead of fr and using align-content solve your problem.

body {
  display: flex;
  justify-content: center;
  margin: 0
  width: 100%;
  height: 100vh;
}

.wrapper {
  display: grid;
  grid-template-columns: auto auto auto;
  align-content : start;
  /*  grid-gap: 1rem;  */ 
  max-width: 1250px;
}

.box {
  width: 200px;
  height: 200px;
  background: red;
  margin: 1rem ; 
}
<div class="wrapper">
  <div class="box"></div>
<!--   <div class="box"></div>
  <div class="box"></div> -->
</div>
Chisholm answered 19/12, 2021 at 8:28 Comment(3)
That doesn't align it to the centre. If you bring up the developer tools it offsets it to the left by 2rem, caused by the grid gap property.Laudable
it's true . You are right. using margin located it exactly in the center.Chisholm
I can't use that solution because when there are two images I need the grid gap and when there are 3 or more I'll then have extra margin I don't need.Laudable

© 2022 - 2024 — McMap. All rights reserved.