skip to Main Content

I’m making a React app which renders xy number of cards and a single widget inside a flex container. All cards have the same width and height, but widget’s height equals to card height * 2 + row-gap. The container’s width varries based on viewport width and should visually look as 2 or 3 columns with cards respectively. I’m providing a mockup image of needed layouts for further clarity, widget is depicted with blue color.

Layout mockup

Whatever I tried, I couldn’t get the widget to display in the correct spot without changing the height of the row to its size, leaving a "blank row" where 1 or 2 more cards should be rendered as displayed on the following image.

enter image description here

My current solution involves Javascript, where based on the viewport width I’m loading 2 or 4 cards into a separate small flex container which is rendered as the first child of the main flex container. This workaround works great visually, but complicates my code a lot seeing as i have to cover many different cases for it to function correctly. I want to achieve the same thing using css/flexbox but I’m still a beginner, have never done a layout like this before and obviously can’t figure out how to do so. Widget can’t be absolutely positioned because that breaks the scroll functionality of its child element.

I’m providing test HTML and CSS measurements in correct ratio in case it helps.

.container {
  margin: 50px 300px;
  display: flex;
  flex-wrap: wrap;
  row-gap: 20px;
  column-gap: 20px;
  min-width: 320px; /* Width for 2 columns */
  max-width: 490px; /* Width for 3 columns */
  border: 3px solid rgb(0, 22, 117);
}

.card {
  width: 150px;
  height: 100px;
  background-color: bisque;
}

.widget {
  height: 220px; /* card height * 2 + row-gap */
  width: 150px;
  background-color: rgb(134, 204, 245);
}
<div class="container">
  <div class="widget"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
  <div class="card"></div>
</div>

<!-- Original jsx
<div className='container'>
  <div className='widget'></div>
  <div className='card'></div>
  <div className='card'></div>
  <div className='card'></div>
  <div className='card'></div>
  <div className='card'></div>
  <div className='card'></div>
  <div className='card'></div>
  <div className='card'></div>
  <div className='card'></div>
  <div className='card'></div>
  <div className='card'></div>
  <div className='card'></div>
  <div className='card'></div>
  <div className='card'></div>
  <div className='card'></div>
  <div className='card'></div>
  <div className='card'></div>
  <div className='card'></div>
</div>
-->

I really want to learn how to create layouts such as this one in flexbox, not only because of my current project, but also to expand my knowledge and understanding in general. Please help me if you have any idea on how to solve this. Thank you in advance 🙂

2

Answers


  1. This can be done using grid-layout without javascript, here’s an example:

    .container {
      margin: 50px 300px;
      display: grid;
      /* auto calculate columns, minimun cell width is 140px */  
      grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
      
      /* height of each cell */
      grid-auto-rows: 100px;
      
      grid-auto-flow: row dense;
      gap: 20px;
      min-width: 320px;
      max-width: 490px;
      border: 3px solid rgb(0, 22, 117);
    }
    
    .card {
      background-color: bisque;
    }
    
    .widget {
      background-color: rgb(134, 204, 245);
      /* widget, span for 2 rows */
      grid-row: auto / span 2;
      
      /* set the widget to the right most column */
      grid-column-end: -1;
    }
    <div class="container">
      <div class="widget"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
    </div>
    
    <!-- Original jsx
    <div className='container'>
      <div className='widget'></div>
      <div className='card'></div>
      <div className='card'></div>
      <div className='card'></div>
      <div className='card'></div>
      <div className='card'></div>
      <div className='card'></div>
      <div className='card'></div>
      <div className='card'></div>
      <div className='card'></div>
      <div className='card'></div>
      <div className='card'></div>
      <div className='card'></div>
      <div className='card'></div>
      <div className='card'></div>
      <div className='card'></div>
      <div className='card'></div>
      <div className='card'></div>
      <div className='card'></div>
    </div>
    -->

    As @ralph.m mentioned. You can always set .widget on the right most column by setting grid-column-end: -1; to your .widget

    Login or Signup to reply.
  2. Flexbox layout is unable to do this, it does not create a 2d grid, you will have to use the gridbox layout., so elements can be spanning rows and columns without leaving a gap.

    here is an example:

    .grid {
    /* give some space gutters */
      margin: 1em auto;
      padding:1em;
      gap: 1em;
     /* build a grid */
      display: grid; 
      grid-auto-flow: row dense;/* fill any holes that could show up */
      grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); /* auto calculate size of the grid */
      grid-auto-rows: 75px; /* give an height row for the demo */
      max-width: 80%;/* whatever */
      border: solid;/* see my boundaries */
    }
    
    .card {
      background-color: salmon;
    }
    
    .bigger {
      background-color: lightblue;
      grid-row: 1/ span 2;/* keep me on first row and lay over 2 rows */
      grid-column-end: -1; /* keep me on the last column no matter how many */
    }
    <div class="grid">
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="bigger">I can be anywhere in the flow but I'll show on top end</div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>
      <div class="card"></div>  
    </div>

    row dense , auto-fill & grid-column-end are the rules/value that are usefull here to use 🙂 among these see : https://developer.mozilla.org/en-US/docs/Web/CSS/grid-column-end

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search