skip to Main Content

I’m building a carousel inside a flex container which has flex-direction: column. I thought this would be trivial, and it probably is. I’ve built carousels by hand before but not in this exact circumstance.

My problem is that the content is a carousel, a scrolling list of items that is in a parent with overflow: hidden, but its size is not being constrained by its flex item parent so it is occupying the full width of all its child items. Normally a parent like that has its size constrained in some way, causing it to be smaller than its content, a very common design pattern.

Consider this snippet, with two columns in the outer layout, a Left panel (yellow) wants to be 30% width and Right panel (green) wants to be 60% width. Inside the left panel, I want a horizontal carousel, which contains many items, only a few of which will be visible because there isn’t space to display them all (I would add proper scrolling mechanics later).

.container {
  display: flex;
}

.left-panel {
  flex: 0 0 33%;
  display: flex;
  flex-direction: column;
  background-color: yellow;
}
.right-panel {
  flex: 1 0 auto;
  flex-basis: 66%;
  background-color: green;
}

.items {
  flex: 1;
  overflow-x: hidden;
}

.items-wrapper {
  display: flex;
  flex-wrap: nowrap;
  gap: 1rem;
}

.item {
  width: 8rem;
  height: 8rem;
  background-color: red;
}
<div class="container">
  <div class="left-panel">
    <div class="title">Left Panel, this panel is supposed to be only 30% the width of the parent, but is being pushed wider by the children below.</div>
    <p>I want the .items div to be the same width as the 33% wide .left-panel, and I'll deal with the scroll/overflow once that is working.</p>

    <div class="items">
      <div class="items-wrapper">
        <div class="item">Item</div>
        <div class="item">Item</div>
        <div class="item">Item</div>
        <div class="item">Item</div>
        <div class="item">Item</div>
        <div class="item">Item</div>
        <div class="item">Item</div>
      </div>
    </div>
  </div>
  
  <div class="right-panel">
    Right Panel Here
  </div>
</div>

See equivalent codepen https://codepen.io/neekfenwick/pen/QWRQrVL

I’ve told the left panel to be flex: 0 0 33% which I thought would force it to be 33% the width of its parent (the document), since flex-grow is 0 and flex-basis is 33%, and any content inside would be sized accordingly.

I understand that the content is physically larger than this requested 33%. How can I make the .items element respect the size requested by its parent?

Perhaps what’s tripping me up is that the .left-panel has flex-direction: column which means the main axis is vertical, so the effect of flex-basis, flex-grow etc becomes confusing, but the flex axis of the outer .container is the default row so I can’t narrow down what’s going wrong.

I’ve found advice at Overflow and contain flex-child of a flex-child advising flex-basis: 0 which doesn’t help.

This is not helped by having a bit of a stinker of a cold right now. Help would be hugely appreciated 🙂

For clarification, here is an example that achieves what I’m trying to do but without using Flexbox. Here, the left and right panels are divs with display: inline-block and fixed widths. See how the .items carousel is constrained within the width of .left-panel, so you cannot see the items that overflow its width.

https://codepen.io/neekfenwick/pen/JjqpZQz

.container {
  /* Container has no flex layout, children have fixed width */
}

.left-panel {
  display: inline-block;
  vertical-align: top;
  width: 33%;
  ffflex-direction: column;
  background-color: yellow;
}
.right-panel {
  display: inline-block;
  vertical-align: top;
  width: 66%;
  background-color: green;
}

.items {
  flex: 1;
  overflow-x: hidden;
}

.items-wrapper {
  display: flex;
  flex-wrap: nowrap;
  gap: 1rem;
}

.item {
  flex: 0 0 8rem;
  width: 8rem;
  height: 8rem;
  background-color: red;
}
<div class="container">
  <div class="left-panel">
    <div class="title">An example based on <a href="https://codepen.io/neekfenwick/pen/QWRQrVL">This Codepen https://codepen.io/neekfenwick/pen/QWRQrVL</a> but without using Flexbox, Left Panel, this panel is set to 33% the width of the parent.</div>
    <p>The .items div is therefore the same width as the 33% wide .left-panel.</p>

    <div class="items">
      <div class="items-wrapper">
        <div class="item">Item</div>
        <div class="item">Item</div>
        <div class="item">Item</div>
        <div class="item">Item</div>
        <div class="item">Item</div>
        <div class="item">Item</div>
        <div class="item">Item</div>
        <div class="item">Item</div>
        <div class="item">Item</div>
      </div>
    </div>
  </div>
  
  <div class="right-panel">
    Right Panel Here
  </div>
</div>

2

Answers


  1. Chosen as BEST ANSWER

    For the record, the error seems to have been the use of width on the .item CSS rule. A better solution is to use flex-basis.

    Here is an example solution, showing two layouts, one with many items in the list and one with just 3. I have sized the item scroller using CSS variables, and calc() to calculate the width required for 3 to be visible, as that is the kind of layout I was aiming for and is more useful in a real-world application.

    :root {
      --item-width: 8rem;
      --item-gap: 1rem;
    }
    
    .container {
      display: flex;
      margin-bottom: 1rem;
    }
    
    .left-panel {
      flex: 0 0 calc(var(--item-width) * 3 + (var(--item-gap)*2));
      display: flex;
      flex-direction: column;
      background-color: yellow;
    }
    .right-panel {
      flex: 1 0 66%;
      background-color: green;
    }
    
    .items {
      overflow-x: scroll;
    }
    
    .items-wrapper {
      display: flex;
      flex-wrap: nowrap;
      gap: var(--item-gap);
    }
    
    .item {
      flex: 0 0 var(--item-width);
      height: var(--item-width);
      background-color: red;
    }
    <div class="container">
      <div class="left-panel">
        <div class="title">This panel has many items.</div>
    
        <div class="items">
          <div class="items-wrapper">
            <div class="item">Item</div>
            <div class="item">Item</div>
            <div class="item">Item</div>
            <div class="item">Item</div>
            <div class="item">Item</div>
            <div class="item">Item</div>
            <div class="item">Item</div>
            <div class="item">Item</div>
          </div>
        </div>
      </div>
      
      <div class="right-panel">
        Right Panel Here
      </div>
    </div>
    
    <div class="container">
      <div class="left-panel">
        <div class="title">This panel has 3 items.</div>
    
        <div class="items">
          <div class="items-wrapper">
            <div class="item">Item</div>
            <div class="item">Item</div>
            <div class="item">Item</div>
          </div>
        </div>
      </div>
      
      <div class="right-panel">
        Right Panel Here
      </div>
    </div>

    See codepen at https://codepen.io/neekfenwick/pen/OJYKEKo

    This solution does still run into problems when the items are smaller, for example when --item-width: 5rem. I don't have a solution for that yet.

    In my real world application I have switched to using CSS grid to control the container, and only using flexbox for the items scroller. This has also allowed me to overlap grid cells with greater control than I found with flexbox.


  2. If i understand you well , You want the left side (control) being only 33% ,
    And items on it should be column direction , if I’m right try this

    .items-wrapper {
      display: flex;
      flex-direction: column;
      align-items: center;
      flex-wrap: nowrap;
      gap: 1rem;
    }
    

    The result :

    .container {
      display: flex;
    }
    
    .left-panel {
      flex-basis:33%;
      display: flex;
      flex-direction: column;
      background-color: yellow;
    }
    .right-panel {
      flex: 1 0 auto;
      flex-basis: 66%;
      background-color: green;
    }
    
    .items {
      flex: 1;
      overflow-x: hidden;
    }
    
    .items-wrapper {
      display: flex;
      flex-direction: column;
      align-items: center;
      flex-wrap: nowrap;
      gap: 1rem;
    }
    
    .item {
      width: 8rem;
      height: 8rem;
      background-color: red;
    }
    <div class="container">
      <div class="left-panel">
        <div class="title">Left Panel, this panel is supposed to be only 30% the width of the parent, but is being pushed wider by the children below.</div>
        <p>I want the .items div to be the same width as the 33% wide .left-panel, and I'll deal with the scroll/overflow once that is working.</p>
    
        <div class="items">
          <div class="items-wrapper">
            <div class="item">Item</div>
            <div class="item">Item</div>
            <div class="item">Item</div>
            <div class="item">Item</div>
            <div class="item">Item</div>
            <div class="item">Item</div>
            <div class="item">Item</div>
          </div>
        </div>
      </div>
      
      <div class="right-panel">
        Right Panel Here
      </div>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search