Search code examples
cssangularscrollflexboxcss-grid

CSS Grid - How to implement grid items, which don't "stretch" the grid row and have a scroll-able container


My challenge is:

I want to have a grid with a fixed amount of columns (which can later be adjusted via javascript) and a flexible amount of rows of equal height.

The number of rows are determined by the amount of grid items, which are UI cards.

These cards should fill out the entire height of their respective cell but MUST not increase the height of the row. So basically max-height = row-height assigned by grid

Then inside these cards we have the typical three parts: Header, Body and Footer. The body MUST be scroll-able, if more list items exists than the row-height allows.

I've tried to implement this on stackblitz

https://stackblitz.com/edit/angular-3gkmtm

What i don't understand is

  1. Why the cards "stretches" the row when more items appear
  2. How to achieve the scroll-able card body section without manually using a fixed height (like in the example i use max-height)
  3. Why when there are more then 3 rows, it overflows

Please help!

<article>
  <section>
    <h2>Fixed Gird with scrollable cards</h2>
  </section>
  <section>
    <button (click)="onAdd()">Add</button>
    <button (click)="onRemove()">Remove</button>
  </section>
    <section class="remaining-height">
        <div class="grid-container">
      <div class="grid-item" *ngFor="let card of cards">
      <div class="card">
        <div class="card-header">Card #{{card}}</div>
        <div class="card-body card-flexible-scroll">
          <div class="list-item" *ngFor="let item of list">{{item}}</div>
        </div>
        <div class="card-footer">
            Some Footer
        </div>
      </div>
      </div>
        </div>
    </section>
</article>


article{
  display: flex;
  flex-direction: column;
  height: 100%;
}
.remaining-height{
  flex:1
}
.grid-container {
    display: grid;
    grid-gap: 0.5rem;
    height: 100%;
    grid-auto-rows: auto;
  grid-template-columns: repeat(5, auto);
}

.grid-item{
  display: flex;
  padding:24px;
}

.card{
  display: flex;
  flex-direction: column;
  width: 100%;
  height:100%;
  background:#ccc;
}
.card-body{
  .list-item{
    padding: 6px;
    background:#fcd3d3;
  }
   .list-item:nth-child(even){
  background:#efefef;
 } 
}
.card-flexible-scroll{
  flex:1;
  overflow-y:auto;
  max-height: 300px; // <= no max height
}

Angular Controller to generate cards and list items

```js
export class AppComponent {
  name = "Angular";
  cards = new Array(8).fill(0).map((_,idx)=>idx+1);
  list = new Array(30).fill(0).map((_,idx)=>idx+1);

  onAdd() {
    this.cards.push(this.cards.length + 1);
  }

  onRemove() {
    this.cards.pop();
  }
}

global style

html , body{
  height: 100vh;
  overflow: hidden;
}

Solution

  • If I understand correctly what you are trying to do you want that the card body takes all the available row space between the header and footer and not force the card to be bigger than the row if it contains items. It is possible to achieve that with adding another div with absolute positioning inside the card body div that is sized to the full body height then the items inside will overflow correctly.

    Here is the changed template:

    <article>
      <section>
        <h2>Fixed Gird with scrollable cards</h2>
      </section>
      <section>
        <button (click)="onAdd()">Add</button>
        <button (click)="onRemove()">Remove</button>
      </section>
        <section class="remaining-height">
            <div class="grid-container">
          <div class="grid-item" *ngFor="let card of cards">
          <div class="card">
            <div class="card-header">Card #{{card}}</div>
            <div class="card-body">
              <div class="card-flexible-scroll">
                <div class="list-item" *ngFor="let item of list">{{item}}</div>
              </div>
            </div>
            <div class="card-footer">
                Some Footer
            </div>
          </div>
          </div>
            </div>
        </section>
    </article>
    

    And the updated CSS:

    .card-body{
      .list-item{
        padding: 6px;
        background:#fcd3d3;
      }
       .list-item:nth-child(even){
        background:#efefef;
      }
      position: relative;
      flex:1;
    }
    .card-flexible-scroll{
      position: absolute;
      width: 100%;
      height: 100%;
      max-height: 100%;
      overflow-y: auto;
    }
    

    I created a fork of your StackBlitz where you can see how it works. If this is not what you look for please explain more.