cssflexbox

How to make flex items wrap until container's height is reached, then shrink on further window resizing?


I'm working on a layout using CSS flexbox and facing an issue where I want my flex items to behave in a specific way when the browser window is resized:

  1. Desired Behaviour:

    • When the window width decreases, the flex items should wrap until their combined height reaches the container's height.

    • Once the container's height is reached, the items should stop wrapping and start shrinking instead.

  2. Current Issue:
    Right now, the items continue wrapping and overflow the parent container's height.

Here is an example code showcasing my issue: Link to Flems.io

.container {
  font-family: arial, sans-serif;
  margin: 0;
  padding: 0;
  list-style-type: none;
  border: 2px dashed red;

  display: flex;
  align-items: flex-start;
  height: 120px;
  flex-wrap: wrap;
}

.flex {
  background: #6ab6d8;
  padding: 10px;
  border: 3px solid #2e86bb;
  color: white;
  font-size: 20px;
  text-align: center;

  width: 400px;
  flex: 0 1 auto;
  min-width: 0;
}

.flex2 {
  width: 300px;
}
<ul class="container">
  <li class="flex flex1">1: some flex item</li>
  <li class="flex flex2">2: some flex item</li>
  <li class="flex flex3">3: some flex item</li>
  <li class="flex flex4">4: some flex item</li>
  <li class="flex flex5">5: some flex item</li>
</ul>

I tried adding min-width: 0 to the flex items because I learned that flexbox sets min-width to auto by default, unlike other block-level elements where min-width is typically 0. However, this change didn’t solve the problem.


Solution

  • Maybe with container queries? We can detect the overflow event with the known widths of the children. A bit hacky, but is there a more natural way to do this without javascript?

    * { box-sizing: border-box; min-width: 0; min-height: 0; }
    
    .container {
      font-family: arial, sans-serif;
      margin: 0;
      padding: 0;
      list-style-type: none;
      border: 2px dashed red;
    
      display: flex;
      align-items: flex-start;
      height: 120px;
      flex-wrap: wrap;
    
      container-type: inline-size;
    }
    
    .flex {
      background: #6ab6d8;
      padding: 10px;
      border: 3px solid #2e86bb;
      color: white;
      font-size: 20px;
      text-align: center;
    
      width: 400px;
      flex: 0 1 auto;
    }
    
    .flex2 {
      width: 300px;
    }
    
    @container (max-width: 1100px) {
      .flex1 { flex-basis: calc(400 / 1100* 100%); }
      .flex2 { flex-basis: calc(300 / 1100* 100%); }
      .flex3 { flex-basis: calc(400 / 1100* 100%); }
      .flex4 { flex-basis: calc(400 / 1100* 100%); }
      .flex5 { flex-basis: calc(400 / 1100* 100%); }
    
      /* Debug color change */
      .flex {
        background-color: aqua;
      }
    }
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
      <link rel="stylesheet" href="stylesheet.css">
    </head>
    <body>
      <ul class="container">
        <li class="flex flex1">1: some flex item</li>
        <li class="flex flex2">2: some flex item</li>
        <li class="flex flex3">3: some flex item</li>
        <li class="flex flex4">4: some flex item</li>
        <li class="flex flex5">5: some flex item</li>
      </ul>
    </body>
    </html>

    P.S: To do this without container queries, it can also be done with grid. Its suggested and explained as a fallback in the documentation.