25

I have a sequence of articles inside a div of 4+ length, without any rounding row tag. I need to represent it as a table of 3 articles (columns) per row, probably with display: grid. Every article has a header, a section and a footer.

How would I implement an equal height for each header, an equal height for each section, and an equal height footer, aligned to the bottom of the article, inside each row of articles? Is it even possible? Should I use display: table?

PS I need to change the number of articles per row dynamically, depending on screen width. Thanx.

HTML:

body {
  width: 100%;
  max-width: 1024px;
  margin: auto;
}

.container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

.container article {
  display: grid;
}

article header {
  background-color: #eeeeee;
}

article section {
  background-color: #cccccc;
}

article footer {
  background-color: #dddddd;
}
<div class="container">
  <article>
    <header>
      <h2>Header</h2>
      <h2>Header</h2>
    </header>
    <section>
      <p>Content</p>
    </section>
    <footer>
      <p>Footer</p>
    </footer>
  </article>
  <article>
    <header>
      <h2>Header</h2>
    </header>
    <section>
      <p>Content</p>
      <p>Content</p>
      <p>Content</p>
      <p>Content</p>
      <p>Content</p>
    </section>
    <footer>
      <p>Footer</p>
      <p>Footer</p>
    </footer>
  </article>
  <article>
    <header>
      <h2>Header</h2>
    </header>
    <section>
      <p>Content</p>
      <p>Content</p>
      <p>Content</p>
    </section>
    <footer>
      <p>Footer</p>
    </footer>
  </article>
  <article>
    <header>
      <h2>Header</h2>
    </header>
    <section>
      <p>Content</p>
      <p>Content</p>
      <p>Content</p>
      <p>Content</p>
    </section>
    <footer>
      <p>Footer</p>
      <p>Footer</p>
    </footer>
  </article>
</div>

NB: JS is deprecated.

https://codepen.io/yudnikov/pen/mBvbGW?editors=1100#0

This solution:

grid-auto-rows: 1fr; 

has been proposed as a duplicate, which it is not. It will only give the articles equal height, while e.g. the headers of each article remains differently sized.

I originally had this problem:

This is what I'am talking about

And the grid-auto-rows: 1fr solution results in this:

Wrong!

Xyz
  • 5,955
  • 5
  • 40
  • 58
Igor Yudnikov
  • 434
  • 1
  • 6
  • 14
  • Use flex instead. With flex the height of a div will take the height of its tallest sibling. – izulito Oct 18 '17 at 18:22
  • 3
    Wrong. Flex supports only single line. I need that when i have 3 articles per row, and header of say first article grows because of two h2 tags, the headers of thge rest articles in the row, and only in this row ghew up to equal height. – Igor Yudnikov Oct 18 '17 at 18:46
  • The solution from https://stackoverflow.com/questions/44488357/equal-height-rows-in-css-grid-layout solves not EXACTS the same problem. When I'll do so, I'll get in my case equal height articles but not equal height article's headers and not equal height article's content! – Igor Yudnikov Oct 19 '17 at 12:43
  • 2
    No way you can do it this way. Use script to equalize headers. – skobaljic Oct 19 '17 at 16:20
  • Thanx. Probably I would make all headers fixed height... – Igor Yudnikov Oct 19 '17 at 16:37
  • https://stackoverflow.com/q/36721216/3597276 – Michael Benjamin Oct 23 '17 at 02:33
  • @skobaljic - you might be interested in the `display: contents` approach. It's not perfect, but it's an OK workaround until subgrids get implemented – Danield Oct 29 '17 at 12:38
  • Nice, thanks. Off topic, but I had problems with it recently: every section element should contain at least one header tag `A section, in this context, is a thematic grouping of content. The theme of each section should be identified, typically by including a heading (h1-h6 element) as a child of the section element.` - specified [here](https://www.w3.org/TR/2014/PR-html5-20140916/sections.html#the-section-element). – skobaljic Oct 30 '17 at 00:47
  • After reading several other Q&A to use grid instead of flex, next I stumble upon this case with the top comment starting "Use flex instead." of course without any notion of the many problematic edge cases of both approaches. – Ingo Steinke Jul 17 '23 at 09:40

2 Answers2

34

Is it even possible?

tldr; Yes.

Codepen Demo #1

Codepen Demo # 2 (Uses SASS and is configurable)


The difficulty here is that each article is a grid within itself and therefore any one article has no knowledge about another. Because of this, there is no way for a component of one article like a header to adjust according to the height of a header in another article.

There is actually a way to pull this off with css grids & without changing any markup!

We could 'flatten' the structure with CSS such that all the components of all the articles are part of just one CSS grid - the article container.

This is possible without even changing the current markup by setting the articles with display: contents

display: contents (caniuse)

From Caniuse:

display: contents causes an element's children to appear as if they were direct children of the element's parent, ignoring the element itself. This can be useful when a wrapper element should be ignored when using CSS grid or similar layout techniques.

So if we set the articles with display: contents

.container article {
  display: contents;
}

Now all of the headers, sections and footer become (direct) grid items (of the container - which has display:grid) which we can arrange using the grid-template-areas property.

.container {
  display: grid;
  grid-column-gap: 1em; /* horizontal gap between articles */
  grid-template-columns: repeat(3, 1fr);

  grid-template-areas: "header1 header2 header3" 
                       "section1 section2 section3"
                       "footer1 footer2 footer3"
                       "header4 header5 header6" 
                       "section4 section5 section6"
                       "footer4 footer5 footer6"
}

Since each header/section/footer take up exactly one cell - this forces them to take up the same vertical height. So e.g. header1,header2 and header3 will all have the same height regardless of their content.

Now set the grid-area properties on each of the cells.

article:nth-child(1) header {
  grid-area: header1;
}
article:nth-child(2) header {
  grid-area: header2;
}
article:nth-child(3) header {
  grid-area: header3;
}
article:nth-child(4) header {
  grid-area: header4;
}
article:nth-child(1) section {
  grid-area: section1;
}
...
article:nth-child(4) section {
  grid-area: section4;
}
article:nth-child(1) footer {
  grid-area: footer1;
}
...
article:nth-child(4) footer {
  grid-area: footer4;
}

Finally, set a vertical gap between each row of articles (starting from the second row of articles):

article:nth-child(n + 4) header {
  margin-top: 1em;
}

Demo:

body {
  width: 100%;
  max-width: 1024px;
  margin: auto;
}

.container {
  display: grid;
  grid-column-gap: 1em;
  grid-template-columns: repeat(3, 1fr);
  grid-template-areas: "header1 header2 header3" 
                      "section1 section2 section3"
                        "footer1 footer2 footer3"
                        "header4 header5 header6" 
                      "section4 section5 section6"
                        "footer4 footer5 footer6"
}

.container article {
  display: contents;
}

article header {
  background-color: #eeeeee;
}

article section {
  background-color: #cccccc;
}

article footer {
  background-color: #dddddd;
}

article:nth-child(n + 4) header {
  margin-top: 1em;
}

article:nth-child(1) header {
  grid-area: header1;
}
article:nth-child(2) header {
  grid-area: header2;
}
article:nth-child(3) header {
  grid-area: header3;
}
article:nth-child(4) header {
  grid-area: header4;
}
article:nth-child(1) section {
  grid-area: section1;
}
article:nth-child(2) section {
  grid-area: section2;
}
article:nth-child(3) section {
  grid-area: section3;
}
article:nth-child(4) section {
  grid-area: section4;
}
article:nth-child(1) footer {
  grid-area: footer1;
}
article:nth-child(2) footer {
  grid-area: footer2;
}
article:nth-child(3) footer {
  grid-area: footer3;
}
article:nth-child(4) footer {
  grid-area: footer4;
}
<div class="container">
    <article>
        <header>
            <h2>Header</h2>
            <h2>Header</h2>
        </header>
        <section>
            <p>Content</p>
        </section>
        <footer>
            <p>Footer</p>
        </footer>
    </article>
    <article>
        <header>
            <h2>Header</h2>
        </header>
        <section>
            <p>Content</p>
            <p>Content</p>
            <p>Content</p>
            <p>Content</p>
            <p>Content</p>
        </section>
        <footer>
            <p>Footer</p>
            <p>Footer</p>
        </footer>
    </article>
    <article>
        <header>
            <h2>Header</h2>
        </header>
        <section>
            <p>Content</p>
            <p>Content</p>
            <p>Content</p>
        </section>
        <footer>
            <p>Footer</p>
        </footer>
    </article>
    <article>
        <header>
            <h2>Header</h2>
        </header>
        <section>
            <p>Content</p>
            <p>Content</p>
            <p>Content</p>
            <p>Content</p>
        </section>
        <footer>
            <p>Footer</p>
            <p>Footer</p>
        </footer>
    </article>
</div>

Codepen Demo)

Of course, instead of using the grid-template-areas + grid-area properties we could instead use the grid-row + grid-column properties to achieve the same result - Codepen demo


Note: I do realise that the above is verbose and not exactly an optimal solution - but my point here is that it is possible. Also, we could use SASS loops to make that code much cleaner and also configurable.

It might be nice if there was some way to repeat a pattern using grid-template-areas - something like:

pseudo-code (not legal):

grid-template-areas: repeat("header1 header2 header3" 
                           "section1 section2 section3"
                           "footer1 footer2 footer3")

...then we could get a more dynamic solution which would work for n articles solution by setting the grid-area using nth-child like:

article:nth-child(3n + 1) header {
  grid-area: header1;
} 

etc. ... but I don't think that's possible at the moment (or maybe it's not necessary because subgrids will be able to do this?)


NB:

Grid Layout Module Level 2 introduces Subgrids which will make this problem easier to solve and without having to use display: contents


Should I use display: table?

For the layout which you need - display:table won't help much. Firstly, you'd have to totally restructure your markup to group article components together as apposed to articles, you'd have to hack around to style the table to look like 'articles' but even then - tables don't wrap so you'd need to wrap every three articles into a separate table.... even if it were possible it would be really messy and hard to maintain.

Danield
  • 121,619
  • 37
  • 226
  • 255
-4

The simplest idea that comes to my mind is to use procentage. For example:

.container{
    width: 30%;
    height: 80%;
}

To have full control over your layout, please check on CSS Default Values Reference:

https://www.w3schools.com/cssref/css_default_values.asp

For diferent devices , just use separate CSS files…

A. K.
  • 40
  • 5