Srikanth Rayabhagi
Srikanth Rayabhagi

Reputation: 1443

Closing gaps in grid layout when grid items are removed

Is there a way to use grid layout with grid gaps and missing grid-areas without leaving extra gaps in the layout.

.main {
  display: grid;
  grid-template-columns: 1fr 1fr;
  width: 500px;
}
.container {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-areas: 'a' 'b' 'c' 'd'; 
  width: 200px;
  grid-gap: 10px;
}

.a, .b, .c, .d {
  border: 1px solid black;
  width: 200px;
}

.a {
  grid-area: a;
}

.b {
  grid-area: b;
}

.c {
  grid-area: c;
}

.d {
  grid-area: d;
}
<div class="main">
  <div class="container">
    <div class="a">A box</div>
    <div class="b">B box</div>
    <div class="d">D box</div>
  </div>

  <div class="container">
    <div class="a">A box</div>
    <div class="b">B box</div>
    <div class="c">C box</div>
    <div class="d">D box</div>
  </div>
</div>

You can see that there is a gap between B and D when C is absent from the layout.

Other than changing to a flex layout (non-grid display), is there a way to avoid this?

Thanks, Srikanth

Upvotes: 2

Views: 1035

Answers (2)

Michael Benjamin
Michael Benjamin

Reputation: 371113

The answer is yes. Remove grid-template-areas and its related grid-area rules.

.main {
  display: grid;
  grid-template-columns: 1fr 1fr;
  width: 500px;
}
.container {
  display: grid;
  grid-template-columns: 1fr;
  /* grid-template-areas: 'a' 'b' 'c' 'd'; */
  width: 200px;
  grid-gap: 10px;
}

.a, .b, .c, .d {
  border: 1px solid black;
  width: 200px;
}

/*
.a { grid-area: a; }
.b { grid-area: b; }
.c { grid-area: c; }
.d { grid-area: d; }
*/
<div class="main">

  <div class="container">
    <div class="a">A box</div>
    <div class="b">B box</div>
    <div class="d">D box</div>
  </div>

  <div class="container">
    <div class="a">A box</div>
    <div class="b">B box</div>
    <div class="c">C box</div>
    <div class="d">D box</div>
  </div>

</div>

When you create a grid using grid-template-areas, keep these points in mind:

  • grid-template-areas creates an explicit grid.
  • A row is created for every string value. Since you have four strings ("a", "b", "c", "d"), that creates four explicit rows. These rows exist with or without grid items.
  • Explanation: As the container creates an explicit grid, it does not factor in the existence of grid items. The container creates rows and columns. As these rows and columns intersect they create cells. Cells can be occupied or unoccupied.
  • Think of it this way: The relationship between grid rows and items is analogous to tracks and trains. The tracks exist with or without trains. In fact, in Grid parlance, columns and rows are known as "tracks". Tracks are just infrastructure.

So your best bet is to switch from explicit rows, which are defined using grid-template-areas and/or grid-template-rows, to implicit rows, which allow the grid algorithm to create row tracks as needed to accommodate items.

If you remove grid-template-areas and grid-area from your ruleset, the grid will automatically lay out the grid items in a single column (as you have defined), and it has no reason to skip rows/leave gaps. Here's the code again from above:

.main {
  display: grid;
  grid-template-columns: 1fr 1fr;
  width: 500px;
}
.container {
  display: grid;
  grid-template-columns: 1fr;
  /* grid-template-areas: 'a' 'b' 'c' 'd'; */
  width: 200px;
  grid-gap: 10px;
}

.a, .b, .c, .d {
  border: 1px solid black;
  width: 200px;
}

/*
.a { grid-area: a; }
.b { grid-area: b; }
.c { grid-area: c; }
.d { grid-area: d; }
*/
<div class="main">

  <div class="container">
    <div class="a">A box</div>
    <div class="b">B box</div>
    <div class="d">D box</div>
  </div>

  <div class="container">
    <div class="a">A box</div>
    <div class="b">B box</div>
    <div class="c">C box</div>
    <div class="d">D box</div>
  </div>

</div>

If you don't want the grid items to consume all free space, then use align-content: start, which overrides the default align-content: stretch:

.main {
  display: grid;
  grid-template-columns: 1fr 1fr;
  width: 500px;
}
.container {
  display: grid;
  grid-template-columns: 1fr;
  /* grid-template-areas: 'a' 'b' 'c' 'd'; */
  align-content: start; /* new */
  width: 200px;
  grid-gap: 10px;
}

.a, .b, .c, .d {
  border: 1px solid black;
  width: 200px;
}

/*
.a { grid-area: a; }
.b { grid-area: b; }
.c { grid-area: c; }
.d { grid-area: d; }
*/
<div class="main">

  <div class="container">
    <div class="a">A box</div>
    <div class="b">B box</div>
    <div class="d">D box</div>
  </div>

  <div class="container">
    <div class="a">A box</div>
    <div class="b">B box</div>
    <div class="c">C box</div>
    <div class="d">D box</div>
  </div>

</div>

Lastly, if you want each implicit row to have a certain height, use grid-auto-rows:

.main {
  display: grid;
  grid-template-columns: 1fr 1fr;
  width: 500px;
}
.container {
  display: grid;
  grid-template-columns: 1fr;
  /* grid-template-areas: 'a' 'b' 'c' 'd'; */
  align-content: start; /* new */
  grid-auto-rows: 40px; /* new */
  width: 200px;
  grid-gap: 10px;
}

.a, .b, .c, .d {
  border: 1px solid black;
  width: 200px;
}

/*
.a { grid-area: a; }
.b { grid-area: b; }
.c { grid-area: c; }
.d { grid-area: d; }
*/
<div class="main">

  <div class="container">
    <div class="a">A box</div>
    <div class="b">B box</div>
    <div class="d">D box</div>
  </div>

  <div class="container">
    <div class="a">A box</div>
    <div class="b">B box</div>
    <div class="c">C box</div>
    <div class="d">D box</div>
  </div>

</div>

Upvotes: 2

Paulie_D
Paulie_D

Reputation: 114990

If you remove the grid-template-areas this works out of the box

.container {
  display: inline-grid;
  grid-template-columns: 1fr;
  width: 200px;
  grid-gap: 10px;
  margin: 1em;
}

.a,
.b,
.c,
.d {
  border: 1px solid black;
  width: 200px;
}
<div class="main">
  <div class="container">
    <div class="a">A box</div>
    <div class="b">B box</div>
    <div class="d">D box</div>
  </div>

  <div class="container">
    <div class="a">A box</div>
    <div class="b">B box</div>
    <div class="c">C box</div>
    <div class="d">D box</div>
  </div>
</div>

Upvotes: 0

Related Questions