1
votes

I'm using Firefox – as yet the only browser to support the subgrid – and actually trying to use that subgrid feature:

const D = document,
  gen = function*() {
    let start = 125;
    while (true) {
      yield ++start;
    }
  },
  imgNum = gen(),
  create = (tag) => document.createElement(tag),
  addCard = (evt) => {
    let article = create('article'),
      main = evt.currentTarget.closest('main'),
      articleCount = main.querySelectorAll('article').length,
      h2 = create('h2'),
      img = create('img'),
      p = create('p');
    h2.textContent = `Title ${articleCount + 1}`;
    img.src = `https://picsum.photos/id/${imgNum.next().value}/200/`;
    p.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!";
    article.append(h2, img, p);
    main.querySelector('section').append(article);
  }

document.querySelector('button').addEventListener('click', addCard);
*, ::before, ::after {
  box-sizing: border-box;
  font-family: Montserrat, Roboto, system-ui;
  font-size: 16px;
  font-weight: 400;
  margin: 0;
  padding: 0;
}

main {
  display: grid;
  margin-block: 1em;
  margin-inline: auto;
  width: clamp(20em, 70vw, 1000px);
}

section {
  display: grid;
  gap: 0.5em;
  grid-auto-columns: 1fr;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: 2em min-content fit-content;
  grid-template-areas: 'header' 'image' 'description';
  padding: 0.5em;
}

article {
  border: 1px solid currentColor;
  display: grid;
  grid-template-rows: subgrid;
  grid-row: auto / span 3;
  padding: 0.5em;
}

article h2 {
  background-color: rgb(210 210 210 / 0.8);
  grid-area: header;
  font-weight: 600;
  text-align: center;
}

article img {
  grid-area: image;
  object-fit: cover;
  width: 100%;
  filter: grayscale(95%);
  opacity: 0.4;
  transition: filter 0.4s ease-in;
}

article p {
  grid-area: description;
}
<main>
  <button>Add new card</button>
  <section>
    <article>
      <h2>Title 1</h2>
      <img src="https://picsum.photos/id/123/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
    <article>
      <h2>Title 2</h2>
      <img src="https://picsum.photos/id/124/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
    <article>
      <h2>Title 3</h2>
      <img src="https://picsum.photos/id/125/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
  </section>
</main>

JS Fiddle demo.

With this, I click the <button> element and a new <article> element is created, giving the following HTML (for the first created <article>):

<article>
  <h2>Title 4</h2>
  <img src="https://picsum.photos/id/125/200/">
  <p>Lorem ipsum dolor sit amet <!-- removed for brevity --></p>
</article>

and that <article> is correctly appended to the <section>. So far, so good.

But, these articles are styled with:

article {
  display: grid;
  grid-template-rows: subgrid;
  grid-row: auto / span 3;
}

And that use of grid-row: auto / span 3 is required to have the <article> span the three rows required to enable the consistent element-sizing of adjacent <article> element's descendants on the same row.

Using the <button> to add another <article>, however, causes some form of visual chaos, in which the <h2> is located behind the <img> element, and the <img> vertically – on the page – lower down than the <p>:

(This is accurately reproduced in the above Snippet, though obviously does require Firefox, version 71 or later.)

As for my expectations: I would like the elements placed on the implicit rows to accurately reflect the layout of the first row, with the explicit rows.

My first attempt to implement this lay in adding:

section {
  /* ... */
  grid-auto-rows: repeat(1, 2em min-content fit-content);
}

This, obviously, did not work.

const D = document,
  gen = function*() {
    let start = 125;
    while (true) {
      yield start++;
    }
  },
  imgNum = gen(),
  create = (tag) => document.createElement(tag),
  addCard = (evt) => {
    let article = create('article'),
      main = evt.currentTarget.closest('main'),
      articleCount = main.querySelectorAll('article').length,
      h2 = create('h2'),
      img = create('img'),
      p = create('p');
    h2.textContent = `Title ${articleCount + 1}`;
    img.src = `https://picsum.photos/id/${imgNum.next().value}/200/`;
    p.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!";
    article.append(h2, img, p);
    main.querySelector('section').append(article);
  }

document.querySelector('button').addEventListener('click', addCard);
*,
 ::before,
 ::after {
  box-sizing: border-box;
  font-family: Montserrat, Roboto, system-ui;
  font-size: 16px;
  font-weight: 400;
  margin: 0;
  padding: 0;
}

main {
  display: grid;
  margin-block: 1em;
  margin-inline: auto;
  width: clamp(20em, 70vw, 1000px);
}

section {
  display: grid;
  gap: 0.5em;
  grid-auto-columns: 1fr;
  grid-auto-rows: repeat(1, 2em min-content fit-content);
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: 2em min-content fit-content;
  grid-template-areas: 'header' 'image' 'description';
  padding: 0.5em;
}

article {
  border: 1px solid currentColor;
  display: grid;
  grid-template-rows: subgrid;
  grid-row: auto / span 3;
  padding: 0.5em;
}

article h2 {
  background-color: rgb(210 210 210 / 0.8);
  grid-area: header;
  font-weight: 600;
  text-align: center;
}

article img {
  grid-area: image;
  object-fit: cover;
  width: 100%;
  filter: grayscale(95%);
  opacity: 0.4;
  transition: filter 0.4s ease-in;
}

article p {
  grid-area: description;
}
<main>
  <button>Add new card</button>
  <section>
    <article>
      <h2>Title 1</h2>
      <img src="https://picsum.photos/id/123/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
    <article>
      <h2>Title 2</h2>
      <img src="https://picsum.photos/id/124/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
    <article>
      <h2>Title 3</h2>
      <img src="https://picsum.photos/id/125/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
  </section>
</main>

JS Fiddle demo.

I'm clearly missing something – but I trust that this use-case has been anticipated – and would very much appreciate help.

1

1 Answers

1
votes

The issue is that you have defined one template which means only one row will follow that template and all the others will have an implicit one. What you need is a repeated template and for this you need to get rid of named areas.

Basically, you don't need to do anything because the browser will do the job for you. Making the article span 3 rows is already enough. Note that your grid-template-rows (2em min-content fit-content) is invalid

const D = document,
    gen = function* (){
    let start = 125;
    while (true){
        yield start++;
    }
  },
  imgNum = gen(),
    create = (tag) => document.createElement(tag),
    addCard = (evt) => {
    let article = create('article'),
      main = evt.currentTarget.closest('main'),
        articleCount = main.querySelectorAll('article').length,
        h2 = create('h2'),
      img = create('img'),
      p = create('p');
  h2.textContent = `Title ${articleCount + 1}`;
  img.src = `https://picsum.photos/id/${imgNum.next().value}/200/`;
  p.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!";
  article.append(h2, img, p);
  main.querySelector('section').append(article);
}

document.querySelector('button').addEventListener('click', addCard);
*, ::before, ::after {
  box-sizing: border-box;
  font-family: Montserrat, Roboto, system-ui;
  font-size: 16px;
  font-weight: 400;
  margin: 0;
  padding: 0;
}

main {
  display: grid;
  margin-block: 1em;
  margin-inline: auto;
  width: clamp(20em, 70vw, 1000px);
}

section {
  display: grid;
  gap: 0.5em;
  grid-template-columns: repeat(3, 1fr);
  padding: 0.5em;
}

article {
  border: 1px solid currentColor;
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 3;
  padding: 0.5em;
}

article h2 {
  background-color: rgb(210 210 210 / 0.8);
  font-weight: 600;
  text-align: center;
  height: 2em;
}

article img {
  object-fit: cover;
  width: 100%;
  height: 100%;
  filter: grayscale(95%);
  opacity: 0.4;
  transition: filter 0.4s ease-in;
}

article p {}
<main>
  <button>Add new card</button>
  <section>
    <article>
      <h2>Title 1</h2>
      <img src="https://picsum.photos/id/123/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
    <article>
      <h2>Title 2</h2>
      <img src="https://picsum.photos/id/124/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
    <article>
      <h2>Title 3</h2>
      <img src="https://picsum.photos/id/125/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
  </section>
</main>

In case you need to define the size of the rows you have to use grid-auto-rows: 1st 2nd 3rd; instead of grid-template-rows: 1st 2nd 3rd;

const D = document,
    gen = function* (){
    let start = 125;
    while (true){
        yield start++;
    }
  },
  imgNum = gen(),
    create = (tag) => document.createElement(tag),
    addCard = (evt) => {
    let article = create('article'),
      main = evt.currentTarget.closest('main'),
        articleCount = main.querySelectorAll('article').length,
        h2 = create('h2'),
      img = create('img'),
      p = create('p');
  h2.textContent = `Title ${articleCount + 1}`;
  img.src = `https://picsum.photos/id/${imgNum.next().value}/200/`;
  p.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!";
  article.append(h2, img, p);
  main.querySelector('section').append(article);
}

document.querySelector('button').addEventListener('click', addCard);
*, ::before, ::after {
  box-sizing: border-box;
  font-family: Montserrat, Roboto, system-ui;
  font-size: 16px;
  font-weight: 400;
  margin: 0;
  padding: 0;
}

main {
  display: grid;
  margin-block: 1em;
  margin-inline: auto;
  width: clamp(20em, 70vw, 1000px);
}

section {
  display: grid;
  gap: 0.5em;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 2em 100px auto;
  padding: 0.5em;
}

article {
  border: 1px solid currentColor;
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 3;
  padding: 0.5em;
}

article h2 {
  background-color: rgb(210 210 210 / 0.8);
  font-weight: 600;
  text-align: center;
}

article img {
  object-fit: cover;
  width: 100%;
  height: 100%;
  filter: grayscale(95%);
  opacity: 0.4;
  transition: filter 0.4s ease-in;
}

article p {}
<main>
  <button>Add new card</button>
  <section>
    <article>
      <h2>Title 1</h2>
      <img src="https://picsum.photos/id/123/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
    <article>
      <h2>Title 2</h2>
      <img src="https://picsum.photos/id/124/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
    <article>
      <h2>Title 3</h2>
      <img src="https://picsum.photos/id/125/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
  </section>
</main>

It should be noted that the result will also look good on browser that doesn't support subgrid since by default all the elements will get placed correctly inside article. You will only miss the gap and the alignment.

The gap can be added to article if you want:

const D = document,
    gen = function* (){
    let start = 125;
    while (true){
        yield start++;
    }
  },
  imgNum = gen(),
    create = (tag) => document.createElement(tag),
    addCard = (evt) => {
    let article = create('article'),
      main = evt.currentTarget.closest('main'),
        articleCount = main.querySelectorAll('article').length,
        h2 = create('h2'),
      img = create('img'),
      p = create('p');
  h2.textContent = `Title ${articleCount + 1}`;
  img.src = `https://picsum.photos/id/${imgNum.next().value}/200/`;
  p.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!";
  article.append(h2, img, p);
  main.querySelector('section').append(article);
}

document.querySelector('button').addEventListener('click', addCard);
*, ::before, ::after {
  box-sizing: border-box;
  font-family: Montserrat, Roboto, system-ui;
  font-size: 16px;
  font-weight: 400;
  margin: 0;
  padding: 0;
}

main {
  display: grid;
  margin-block: 1em;
  margin-inline: auto;
  width: clamp(20em, 70vw, 1000px);
}

section {
  display: grid;
  gap: 0.5em;
  grid-template-columns: repeat(3, 1fr);
  grid-auto-rows: 2em 100px auto;
  padding: 0.5em;
}

article {
  border: 1px solid currentColor;
  display: grid;
  grid-template-rows: subgrid;
  grid-row: span 3;
  padding: 0.5em;
  gap: inherit;
}

article h2 {
  background-color: rgb(210 210 210 / 0.8);
  font-weight: 600;
  text-align: center;
}

article img {
  object-fit: cover;
  width: 100%;
  height: 100%;
  filter: grayscale(95%);
  opacity: 0.4;
  transition: filter 0.4s ease-in;
}

article p {}
<main>
  <button>Add new card</button>
  <section>
    <article>
      <h2>Title 1</h2>
      <img src="https://picsum.photos/id/123/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
    <article>
      <h2>Title 2</h2>
      <img src="https://picsum.photos/id/124/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
    <article>
      <h2>Title 3</h2>
      <img src="https://picsum.photos/id/125/200/" alt="">
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Blanditiis maxime corporis eius qui eligendi, autem tenetur consectetur, eum officiis impedit dolorum ipsum ullam illum at alias hic facilis! Impedit, sint!</p>
    </article>
  </section>
</main>