CSS Grid - repeatable grid-template-areas
Asked Answered
C

3

18

Let's say we have a list of news entries with 7 items.

I've created a pattern with CSS Grid that that should repeat itself after 6 items.

@supports (display: grid)
{
  .list
  {
    display: grid;
    grid-gap: 25px;
    grid-template-columns: repeat(4, 1fr);
    grid-template-rows: auto;
    grid-template-areas:
    "bigLeft bigLeft right1 right2"
    "bigLeft bigLeft bigRight bigRight"
    "left1 left2     bigRight bigRight";
  }

  .item:nth-of-type(6n+1)
  {
    grid-area: bigLeft;
  }
  .item:nth-of-type(6n+2)
  {
    grid-area: right1;
  }
  .item:nth-of-type(6n+3)
  {
    grid-area: right2;
  }
  .item:nth-of-type(6n+4)
  {
    grid-area: left1;
  }
  .item:nth-of-type(6n+5)
  {
    grid-area: left2;
  }
  .item:nth-of-type(6n+6)
  {
    grid-area: bigRight;
  }
}

desired grid-template-areas pattern:

desired grid-template-areas pattern

Now I want this pattern repeating and repeating the more items added to the list. But HERE you can see as soon as an 7th item is added the pattern will not continued but replaced.

I also tried this with not named areas

.item:nth-of-type(6n+1) {
  grid-column: 1 / 3;
  grid-row: 1 / 3;
}
.item:nth-of-type(6n+2) {
  grid-column: 3 / 4;
  grid-row: 1 / 2;
}
.item:nth-of-type(6n+3) {
  grid-column: 4 / 5;
  grid-row: 1 / 2;
}
.item:nth-of-type(6n+4) {
  grid-column: 1 / 2;
  grid-row: 3 / 4;
}
.item:nth-of-type(6n+5) {
  grid-column: 2 / 3;
  grid-row: 3 / 4;
}
.item:nth-of-type(6n+6) {
  grid-column: 3 / 5;
  grid-row: 2 / 4;
}

But same result...

I don´t find any solutions in the specs to accomplish "repeatable grid-template-areas"

Did anyone of you have an idea?

Capriccioso answered 31/10, 2017 at 16:16 Comment(4)
I don't believe you can repeat the grid-template-areas pattern with pure CSS. You would need to use a script to alter the rules to create new grid area names and space for items coming after #6.Polysyllable
Instead of repeating the pattern of grid items, have you considered repeating the grid itself? In other words the 7th item would start a new grid of 6 items.Polysyllable
Here's a rough draft of what I mean: codepen.io/anon/pen/qVdpxy?editors=1100Polysyllable
@Michael_B Thanks for your answers. both possibilities do not seem to me really clean, I think that css grid should be able to cover something like that, maybe just nobody has come up with it, I did not read anything like that in the specs, not even in CSS Grid Level 2.Capriccioso
T
26

grid-templates-areas overides the grid-template-rows & -columns. You have to choose, one or the other way to describe your grid layout.

For a repeating pattern, you can use :nth-child(n) and reset the spanning values : ( https://codepen.io/gc-nomade/pen/qVdpwL ) or snippet below

.grid {
  width: 1000px;
  margin: 0 auto;
  display: grid;
  grid-gap: 25px;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: 200px;
  padding: 10px;
  counter-reset:div
}
.item:nth-child(6n + 4),
.item:nth-child(6n + 1) {
  grid-column: auto /span 2;
  grid-row: auto /span 2;
}
.item {
  border: solid;
  display:flex;
}
.item:before {
  counter-increment:div;
  content:counter(div);
  margin:auto;
  font-size:40px;
}
<div class="grid">
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
  <div class="item"></div>
</div>

if your elements comes randomly but needs to be placed at specific spots in the grid (6th should be at 4th position and spanning) then you will need to set an order value for each of them :( .... there you'll need to relay on javascript if contents and orders may varies

Tatterdemalion answered 31/10, 2017 at 17:5 Comment(2)
grid-templates-areas does not override grid-template-x as you can use both to define how the space is distributed in the areas defined: gridTemplateColumns: "3fr 1.1fr 1.8fr"Lexi
@Lexi unless grid-template-areas and grid-template-columns do notdefine the same amount of columns. if it is not coherent, the last rule should overide the previous, if it doesn't , you get to have troubles to set element into the right areas/columns ... beside, to avoid confusion from coder or browser, the less you write, the clearest it is ;) But feel free to update/improve my answer linking to the spec. I will appreciate . i never found it clearly written anywhereTatterdemalion
H
2

You can achieve exactly what you want just in CSS by using explicit column placement and automatic dense row placement:

let list = document.getElementById("list");
for (var i = 1; i <= 100; i++) {
  let element = document.createElement("div");
  element.className = "item";
  element.textContent = i;
  list.appendChild(element);
}
@supports (display: grid) {
  .list {
    width: 80%;
    margin: 0 auto;
    display: grid;
    grid-gap: 25px;
    grid-template-columns: repeat(4, 1fr);
    grid-template-areas:
      "bigLeft bigLeft   right1   right2"
      "bigLeft bigLeft   bigRight bigRight"
      "left1   left2     bigRight bigRight";
    grid-auto-flow: row dense;
  }
  .item {
    min-height: 200px;
  }
  .item:nth-of-type(6n + 6),
  .item:nth-of-type(6n + 1) {
    min-height: 400px;
  }
  .item:nth-of-type(6n + 1) {
    grid-area: auto / bigLeft / span 2;
    background: red;
  }
  .item:nth-of-type(6n + 2) {
    grid-area: auto / right1;
    background: gray;
  }
  .item:nth-of-type(6n + 3) {
    grid-area: auto / right2;
    background: yellow;
  }
  .item:nth-of-type(6n + 4) {
    grid-area: auto / left1;
    background: blue;
  }
  .item:nth-of-type(6n + 5) {
    grid-area: auto / left2;
    background: lightgray;
  }
  .item:nth-of-type(6n + 6) {
    grid-area: auto / bigRight / span 2;
    background: purple;
  }
}
<div class="list" id="list">
</div>

The areas are not actually repeating here. The areas are creating implicit named lines, suffixed with -start and -end. grid-area: auto / bigLeft / span 2 is shorthand for grid-row: auto / span 2; grid-column: bigLeft. grid-column: bigLeft will find the implicit named lines bigLeft-start and bigLeft-end and use those for the column start and end. So we are not really using the areas per se. We could just as easily have used line numbers and left out grid-template-areas altogether.


Now to answer the title of repeating grid-template-areas:

Grid areas cannot repeat but you can repeat named lines and get a similar effect. Repeating rows requires either a fixed number of repeats or a definite height on the grid container which is not really that useful in this case (I am assuming the list has a variable number of items in it).

For example:

let list = document.getElementById("list");
for (var i = 0; i < 60; i++) {
  let element = document.createElement("div");
  element.className = "item";
  element.textContent = i + 1;
  let area;
  switch (i % 6) {
    case 0:
      area = "bigLeft";
      break;
    case 1:
      area = "right1";
      break;
    case 2:
      area = "right2";
      break;
    case 3:
      area = "left1";
      break;
    case 4:
      area = "left2";
      break;
    case 5:
      area = "bigRight";
      break;
  }
  let num = Math.floor(i / 6) + 1;
  element.style = `grid-row: ${num} ${area}-start / ${num} ${area}-end`;
  list.appendChild(element);
}
@supports (display: grid) {
  .list {
    width: 80%;
    margin: 0 auto;
    display: grid;
    grid-gap: 25px;
    grid-template-columns: [bigLeft-start left1-start] 1fr [left1-end left2-start] 1fr [bigLeft-end left2-end right1-start bigRight-start] 1fr [right1-end right2-start] 1fr [right2-end bigRight-end];
    grid-template-rows: repeat(10, [bigLeft-start right1-start right2-start] minmax(200px, 1fr) [right1-end right2-end bigRight-start] minmax(200px, 1fr) [bigLeft-end left1-start left2-start] minmax(200px, 1fr) [left1-end left2-end bigRight-end]);
  }
  .item {
    min-height: 200px;
  }
  .item:nth-of-type(6n+6),
  .item:nth-of-type(6n+1) {
    min-height: 400px;
  }
  .item:nth-of-type(6n+1) {
    grid-column: bigLeft;
    background: red;
  }
  .item:nth-of-type(6n+2) {
    grid-column: right1;
    background: gray;
  }
  .item:nth-of-type(6n+3) {
    grid-column: right2;
    background: yellow;
  }
  .item:nth-of-type(6n+4) {
    grid-column: left1;
    background: blue;
  }
  .item:nth-of-type(6n+5) {
    grid-column: left2;
    background: lightgray;
  }
  .item:nth-of-type(6n+6) {
    grid-column: bigRight;
    background: purple;
  }
}
<div class="list" id="list">
</div>

This shows that named lines can repeat and items can be positioned using the nth named grid line but that has to be done via JavaScript (sadly the counter function cannot be used here).

The biggest issue with this is that it has a fixed number of repeating rows which is unlikely to be very useful. If we set this to auto-fit/auto-fill then it does not work unless the grid container has a definite height, min-height, or max-height.

Halley answered 10/11, 2022 at 23:26 Comment(0)
A
-1

I had the same issue. I just resolved it by repeating the whole 1-row grid with the container and its item in it over and over.

.grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-template-areas:
    "item item item item"; /* or whatever is needed */
}

.item {
  grid-area: item;
}

<div class="grid">
 <div class="item"></div>
</div>
<div class="grid">
 <div class="item"></div>
</div>
<div class="grid">
 <div class="item"></div>
</div>
....
Anaclinal answered 12/8, 2022 at 13:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.