pjk_ok
pjk_ok

Reputation: 967

Centre CSS Grid Items Dependent On Dynamic Content Count

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>

Upvotes: 7

Views: 1951

Answers (3)

Temani Afif
Temani Afif

Reputation: 273649

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>

Upvotes: 8

the Hutt
the Hutt

Reputation: 18418

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>

Upvotes: 0

Ahmad MRF
Ahmad MRF

Reputation: 1384

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>

Upvotes: -2

Related Questions