Search code examples
htmlcssflexbox

css flex row with one element using horizontal scroll


I want to create a flex row with 2 child elements. Left and Right where:

  • the right side will contain a couple buttons stacked and its width will not change
  • the left side will have a dynamic number of "boxes" that the user can add and remove. (the purpose of the "buttons" is to add / remove boxes)
  • I want the left element to scroll horizontally if its contents take up more room than the remaining space in the row as well as resize if browser/window is resized by user.
  • the left boxes themselves cannot resize, they need to remain a specific width (ie: flex-shrink: 0)

I have set up a simple test for this scenario but I cant get it to work:

      <div class="featureWrap">
        ROW 1
        <div class="featureRow">
          <div class="rowLeft">
            <div class="boxLeft">box 1</div>
            <div class="boxLeft">box 2</div>
            <div class="boxLeft">box 3</div>
          </div>
          <div class="rowRight">
            <div class="boxRight">button 1</div>
            <div class="boxRight">button 2</div>
          </div>
        </div>
        ROW 2 - overflows remaining space
        <div class="featureRow">
          <div class="rowLeft">
            <div class="boxLeft">box 1</div>
            <div class="boxLeft">box 2</div>
            <div class="boxLeft">box 3</div>
            <div class="boxLeft">box 4</div>
            <div class="boxLeft">box 5</div>
            <div class="boxLeft">box 6</div>
            <div class="boxLeft">box 7</div>
            <div class="boxLeft">box 8</div>
            <div class="boxLeft">box 9</div>
          </div>
          <div class="rowRight">
            <div class="boxRight">button 1</div>
            <div class="boxRight">button 2</div>
          </div>
        </div>
      </div>

with the following css

.featureWrap {
  display: flex;
  flex-direction: column;
  margin: 30px;
}

.featureRow {
  display: flex;
  flex-direction: row;
  padding: 5px;
  border: solid 1px rgb(236, 158, 40);
  margin: 10px 0;
}

.rowLeft {
  display: flex;
  flex-direction: row;
  flex-shrink: 0;
  padding: 5px;
  border: solid 1px rgb(83, 176, 230);
  overflow-x: auto;
}

.rowRight {
  display: flex;
  flex-direction: column;
  padding: 5px;
  border: solid 1px rgb(51, 212, 64);
}

.boxLeft {
  width: 100px;
  height: 20px;
  background-color: rgb(152, 215, 226);
  border: solid 1px #555;
}

.boxRight {
  width: 100px;
  height: 20px;
  background-color: greenyellow;
  border: solid 1px #555;
}

here is a stack blitz to the working sample.

can someone help me figure out how to get the left side to scroll horizontally, even with page resizing


Solution

  • Is this the kind of design goal you're looking to accomplish?

    Using overflow: auto and display: flex on .left lets the element scroll. I also extend the width of .right on hover so it's more obvious the .left element is only consuming leftover space after the .right element is calculated.

    This is due to the flex-grow: 1 of .left and flex-shrink: 0 of .right.

    * {
      box-sizing: border-box;
    }
    
    body {
      margin: 0;
    }
    
    .flex-wrap {
      display: flex;
    }
    
    .left, .right {
      border: 5px solid;
      display: flex;
    }
    
    .left {
      flex-grow: 1;
      border-color: hotpink;
      overflow: auto;
    }
    
    .right {
      flex-direction: column;
      justify-content: space-evenly;
      width: 30vw;
      border-color: salmon;
      transition: width 0.2s;
      flex-shrink: 0;
    }
    
    .right:hover {
      width: 50vw;
    }
    
    .box {
      height: 127px;
      width: 127px;
      flex-shrink: 0;
      border: 5px solid;
      display: inline-block;
    }
    
    button {
      padding: 1rem;
      font-size: 18pt;
    }
    <div class="flex-wrap">
      <div class="left">
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
        <div class="box"></div>
      </div>
      <div class="right">
        <button>does</button>
        <button>nothing</button>
      </div>
    </div>

    EDIT: Based on comment, modified to include manual width instead of aspect ratio defined width.