88

I have created a responsive layout for an app using Flexbox. The layout calls for a collapsible menu on the left, a block with a header and body in the middle and a toggleable help-pane on the right (there's more to it but that's the basic structure).

The left menu has two states: 180px wide or 80 px wide. The help pane is either hidden, or takes 180px. The middle box takes the rest of the space. Flexbox works like a charm.

The trouble starts when I make a scrolling div using white-space: nowrap. I have a bunch of items that need to be displayed in a horizontal scroller, so I have a list div with the items, set to overflow:auto and white-space: nowrap.

Usually this works like a charm, but now it breaks my flex layout. Instead of taking the width of the parent (flex) div, the scroller makes the div wider, which in turn pushes the help-pane out of bounds.


The following fiddle illustrates this issue:

http://jsfiddle.net/PieBie/6y291fud/

You can toggle the help-pane by clicking toggle help in the menu bar. Recreate the issue by clicking list whitespace toggle in the menu, this toggles the white-space: no-wrap CSS property of the list. If the help-pane is open, you can see it gets pushed out of bounds.

The bottom list is what I want to achieve, but I want it to be full width of its parent.

I can recreate the issue in Chrome, Firefox, Opera, Vivaldi and Edge. Internet Explorer 11 plays nice (°_°). I would prefer a pure CSS solution (SCSS is also an option), but if need be I can use JS.


$('#nav-toggle').on('click',function(){
 $(this).parent().toggleClass('collapsed');
});
$('#help-toggle').on('click',function(){
 $('#help-pane').toggleClass('visible');
});
$('#list-toggle').on('click',function(){
 $('#list').toggleClass('nowrap');
});
body,html{width:100%;height:100%;overflow:hidden;}

#body{
  display:flex;
  flex-flow:row nowrap;
  position:absolute;
  top:0;
  left:0;
  margin:0;
  padding:0;
  width:100%;
  height:100%;
  background-color:#abc;
  overflow:hidden;
}

#shell{
  flex: 1 1 auto;
  display:flex;
  flex-flow:row nowrap;
  position:relative;
  width:100%;
  min-height:100%;
}

  #left{
    flex: 0 0 180px;
    min-height:100%;
    min-width: 0;
    background:lightblue;
  }
  #left.collapsed{
    flex: 0 0 80px;
  }
  
  #mid{
    flex: 1 1 auto;
    min-height:100%;
    min-width: 0;
    display:flex;
    flex-flow:column nowrap;
    align-items:stretch;
    align-content:stretch;
    position:relative;
    width:100%;
    min-height:100vh;
    min-width: 0;
    background:purple;
  }
      #mid-top{
        flex: 0 0 auto;
        min-height:100px;
        background:green;
      }
      #mid-bottom{
        min-height:calc(100% - 100px);
        flex: 1 1 auto;
        background:lightgreen;
      }
      #list{
        overflow: auto;
        width: 100%;
        max-width: 100%;
      }
      #list.nowrap{
        white-space: nowrap;
      }
      #secondlist{
        overflow: auto;
        max-width: 250px;
        white-space: nowrap;
      }
      .list-item{
        display: inline-block;
        width: 50px;
        height: 50px;
        margin: 2px;
        background: purple;
      }
      .list-item.odd{
        background: violet;
      }
      
#help-pane{
  display:none;
  flex: 0 0 0px;
  background:red;
}
#help-pane.visible{
  display:inherit;
  flex:0 0 180px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="body">
 <div id="shell">
      <div id="left">
          <div id="nav">
            - menu -
          </div>
          <div id="help-toggle">
            help toggle
          </div>
          <div id="nav-toggle">
            nav toggle
          </div>
          <div id="list-toggle">
            list whitespace toggle
          </div>
      </div>
      <div id="mid">
          <div id="mid-top">
                - mid top -
          </div>
          <div id="mid-bottom">
               - mid bottom- <br><br>
               <div id="list">
                 <div class="list-item odd">&nbsp;</div>
                 <div class="list-item">&nbsp;</div>
                 <div class="list-item odd">&nbsp;</div>
                 <div class="list-item">&nbsp;</div>
                 <div class="list-item odd">&nbsp;</div>
                 <div class="list-item">&nbsp;</div>
                 <div class="list-item odd">&nbsp;</div>
                 <div class="list-item">&nbsp;</div>
                 <div class="list-item odd">&nbsp;</div>
                 <div class="list-item">&nbsp;</div>
                 <div class="list-item odd">&nbsp;</div>
               </div>
               <hr>
               <div id="secondlist">
                 <div class="list-item odd">&nbsp;</div>
                 <div class="list-item">&nbsp;</div>
                 <div class="list-item odd">&nbsp;</div>
                 <div class="list-item">&nbsp;</div>
                 <div class="list-item odd">&nbsp;</div>
                 <div class="list-item">&nbsp;</div>
                 <div class="list-item odd">&nbsp;</div>
                 <div class="list-item">&nbsp;</div>
                 <div class="list-item odd">&nbsp;</div>
                 <div class="list-item">&nbsp;</div>
                 <div class="list-item odd">&nbsp;</div>
               </div>
          </div>
      </div>
 </div>
 <div id="help-pane" class="visible">
   - help-pane -
 </div>
</div>
Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
PieBie
  • 3,259
  • 2
  • 18
  • 27
  • The most efficient solution as for now is using `-webkit-line-clamp`: https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-line-clamp You can specify number of lines to preserve, for example 1. Old solution based on `min-width: 0` does not work on complex layouts with wide usage of `flex`. – Alex Vovchuk Aug 11 '23 at 00:18

1 Answers1

190

This is caused by the default flex-box behaviour, which prevents flex-boxes of becoming smaller than it's contents.

The solution to this issue is setting min-width: 0 (or min-height: 0 for columns) to all parent flex-boxes. In this specific case (and in the fiddle):

#shell{
  flex: 1 1 auto;
  display:flex;
  flex-flow:row nowrap;
  position:relative;
  width:100%;
  min-height:100%;
  min-width: 0; /* this one right here does it!*/
} 

$('#nav-toggle').on('click',function(){
 $(this).parent().toggleClass('collapsed');
});
$('#help-toggle').on('click',function(){
 $('#help-pane').toggleClass('visible');
});
$('#list-toggle').on('click',function(){
 $('#list').toggleClass('nowrap');
});
body,html{width:100%;height:100%;overflow:hidden;}

#body{
  display:flex;
  flex-flow:row nowrap;
  position:absolute;
  top:0;
  left:0;
  margin:0;
  padding:0;
  width:100%;
  height:100%;
  background-color:#abc;
  overflow:hidden;
}

#shell{
  flex: 1 1 auto;
  display:flex;
  flex-flow:row nowrap;
  position:relative;
  width:100%;
  min-height:100%;
  min-width: 0;
}

  #left{
    flex: 0 0 180px;
    min-height:100%;
    min-width: 0;
    background:lightblue;
  }
  #left.collapsed{
    flex: 0 0 80px;
  }
  
  #mid{
    flex: 1 1 auto;
    min-height:100%;
    min-width: 0;
    display:flex;
    flex-flow:column nowrap;
    align-items:stretch;
    align-content:stretch;
    position:relative;
    width:100%;
    min-height:100vh;
    min-width: 0;
    background:purple;
  }
      #mid-top{
        flex: 0 0 auto;
        min-height:100px;
        background:green;
      }
      #mid-bottom{
        min-height:calc(100% - 100px);
        flex: 1 1 auto;
        background:lightgreen;
      }
      #list{
        overflow: auto;
        width: 100%;
        max-width: 100%;
      }
      #list.nowrap{
        white-space: nowrap;
      }
      #secondlist{
        overflow: auto;
        max-width: 250px;
        white-space: nowrap;
      }
      .list-item{
        display: inline-block;
        width: 50px;
        height: 50px;
        margin: 2px;
        background: purple;
      }
      .list-item.odd{
        background: violet;
      }
      
#help-pane{
  display:none;
  flex: 0 0 0px;
  background:red;
}
#help-pane.visible{
  display:inherit;
  flex:0 0 180px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="body">
 <div id="shell">
      <div id="left">
          <div id="nav">
            - menu -
          </div>
          <div id="help-toggle">
            help toggle
          </div>
          <div id="nav-toggle">
            nav toggle
          </div>
          <div id="list-toggle">
            list whitespace toggle
          </div>
      </div>
      <div id="mid">
          <div id="mid-top">
                - mid top -
          </div>
          <div id="mid-bottom">
               - mid bottom- <br><br>
               <div id="list">
                 <div class="list-item odd">&nbsp;</div>
                 <div class="list-item">&nbsp;</div>
                 <div class="list-item odd">&nbsp;</div>
                 <div class="list-item">&nbsp;</div>
                 <div class="list-item odd">&nbsp;</div>
                 <div class="list-item">&nbsp;</div>
                 <div class="list-item odd">&nbsp;</div>
                 <div class="list-item">&nbsp;</div>
                 <div class="list-item odd">&nbsp;</div>
                 <div class="list-item">&nbsp;</div>
                 <div class="list-item odd">&nbsp;</div>
               </div>
               <hr>
               <div id="secondlist">
                 <div class="list-item odd">&nbsp;</div>
                 <div class="list-item">&nbsp;</div>
                 <div class="list-item odd">&nbsp;</div>
                 <div class="list-item">&nbsp;</div>
                 <div class="list-item odd">&nbsp;</div>
                 <div class="list-item">&nbsp;</div>
                 <div class="list-item odd">&nbsp;</div>
                 <div class="list-item">&nbsp;</div>
                 <div class="list-item odd">&nbsp;</div>
                 <div class="list-item">&nbsp;</div>
                 <div class="list-item odd">&nbsp;</div>
               </div>
          </div>
      </div>
 </div>
 <div id="help-pane" class="visible">
   - help-pane -
 </div>
</div>
PieBie
  • 3,259
  • 2
  • 18
  • 27
  • 1
    I created a fiddle https://jsfiddle.net/c08hey1a/ – Sviatoslav Oleksiv Dec 19 '18 at 11:38
  • @SteveCahn: it is the default as defined in the [spec](https://www.w3.org/TR/css-flexbox-1/#min-size-auto). – PieBie Jul 08 '19 at 07:08
  • it works but how you make the min width working if you set 0 – luky May 14 '20 at 08:22
  • @luky: You just need to set a value for min-width so it doesn't default to 'box content'. So you can set it to whatever value you like. – PieBie May 14 '20 at 08:46
  • 1
    It may be worth highlighting the phrase "to all parent flex-boxes" and pointing a big arrow at "all". If your flexbox container is nested within other flexbox parents, each will need the `min-width:0` style applied. – timray Aug 26 '21 at 13:22
  • Part 1: This isn't really a flexbox issue, it's more that nowrap content sets the minimum width to the unwrapped content which is why it's really long and causes overflow. For a non-flexbox example let's say you have a container div and it's child has the nowrap content. Intuitively you'd think setting container div's `width: 100%` will fix issue because it will set width same as parent element. But that won't work because the minimum width of the container div is now set to nowrap child content. That's why setting `min-width: 0` to container div fixes it. – Caleb Taylor Jan 20 '22 at 03:30
  • Part 2: I guess the take away with percentage value widths such as `width: 80%` means that take 80% of parent's width as long as minimum width content allows it. – Caleb Taylor Jan 20 '22 at 03:40
  • The most efficient solution as for now is using `-webkit-line-clamp`: https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-line-clamp You can specify number of lines to preserve, for example 1. Old solution based on `min-width: 0` does not work on complex layouts with wide usage of `flex`. – Alex Vovchuk Aug 11 '23 at 00:18