How to stack unknown number of generic content items in a CSS Grid template area

Viewed 73

I haven't quite got my head around grid-template-areas, I suspect. Or maybe I do, but it is the behaviour of direct Grid descendant nodes that do not have a grid-area assigned.

Here is visually what I am trying to achieve:

visual example of desired outcome

Codepen is here: https://codepen.io/davewallace/pen/abYxWxE

Similar code example:

html, body , .container {
  height: 100%;
  margin: 0;
}

.container {
  display: grid;
  grid-template-rows: auto 1fr;
  grid-template-columns: 1fr max-content;
  gap: 40px 40px;
  grid-template-areas:
    "header header"
    ". graphic";
}
.header { grid-area: header; }
.graphic { grid-area: graphic; }

/* For presentation only, no need to copy the code below */
.container * {
  border: 1px solid red;
  position: relative;
}
<div class="container">
  <div class="header">Full-width title</div>
  <div class="graphic">Graphic</div>
  <h4>Random element here</h4>
  <p>Random element here</p>
  <p>Random element here</p>
</div>

I seem to be able to only achieve two things currently:

  1. Everything is in the right place, but all "generic content items" overlap on top of each other, where instead I want them stacked.
  2. The title is in the right place, the image is sort of in the right place, but columns and/or rows are added automatically and do things like wrap under the image

The MDN docs for grid-template-area haven't really cleared the mix of behaviours up for me. I also tried using https://grid.layoutit.com/ to visualise the layout, and that seemed to get me off to a good start, but the default behaviour of the generic nodes has me confused.

2 Answers

You can approximate it like below:

body {
  margin: 0;
}

.container {
  min-height: 100vh;
  display: grid;
  grid-template-columns: 1fr max-content;
  grid-auto-flow: dense;
  gap: 0 40px; /* set only column gap */
}

.header {
  grid-row: 1;
  grid-column: 1/-1;
}

.graphic {
  grid-column: 2;
  grid-row: 2/ span 100; /* this will do the trick */
}

.container *:not(.graphic,:last-child) {
  margin-bottom: 40px; /* replace the row gap */
}


/* For presentation only, no need to copy the code below */

.container * {
  border: 1px solid red;
  position: relative;
  margin: 0;
}
<div class="container">
  <div class="header">Full-width title</div>
  <div class="graphic">Some Graphic here</div>
  <h4>Random element here</h4>
  <p>Random element here</p>
  <p>Random element here</p>
</div>

I was trying to solve your CSS problem but it looks like there is no straightforward solution to it. According to the CSS spec, You cannot span a grid item to occupy all the rows/columns in an implicit grid.

Refer to this answer for more info - span grid item to all rows/columns

But I've come to a solution that could fix your grid by introducing a parent container for the random elements. In that way, you will only need two rows and two columns.

.container {
  display: grid;
  grid-template-columns: 1fr max-content;
  gap: 40px 40px;
  grid-template-areas: "header header" ". graphic";
}

.header {
  grid-area: header;
}

.graphic {
  grid-area: graphic;
}


/* Just to highlight */

.container * {
  border: 1px solid;
}
<div class="container">
  <div class="header">Full-width title</div>
  <div class="graphic">Graphic</div>
  <div class="random">
    <h4>Random element here1</h4>
    <p>Random element here2</p>
    <p>Random element here3</p>
    <p>Random element here4</p>
    <p>Random element here4</p>
    <p>Random element here4</p>
  </div>
</div>

Related