Search code examples
htmlcsslayoutflexbox

How can I reduce the space between the steps and the connecting line in my flexbox layout?


I'm working on a web layout where I need to display a series of steps connected by a line. I've set up a flexbox layout with spheres representing each step and a line connecting them. However, there's too much space horizontally between the line and the spheres, while I want to bring the line closer to the spheres. Ideally it should be only a few pixels between them.

Here's the HTML and CSS code I'm using:

.steps-container,
.label-container {
  display: flex;
  flex-direction: row;
  justify-content: center;
}

.step {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.sphere {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 50px;
  height: 50px;
  border-radius: 50%;
  background-color: lightgrey;
  color: white;
  font-size: 32px;
}

.line {
  position: relative;
  top: 25px;
  height: 3px;
  background-color: lightgrey;
  flex-grow: 1;
  max-width: 150px;
}

.label {
  text-align: center;
  position: relative;
}

.spacer {
  flex-grow: 1;
  max-width: 150px;
}
<div class="steps-container">
  <div class="step">
    <div class="sphere">1</div>
    <div class="label">First step label</div>
  </div>
  <div class="line">&nbsp;</div>
  <div class="step">
    <div class="sphere">2</div>
    <div class="label">Second step label</div>
  </div>
</div>

I suspect the problem lies in my .line CSS class.

I didn't manage to resolve the problem and I'm starting to doubt it is even possible to do what I'm trying to do. I tried moving the line using position: relative but I can't adjust the length of the line that way. I also tried using position: absolute but I just can't get the result I'm expecting and I don't really know how to position the line using absolute values correctly.


Solution

  • The line is as wide as it can be with basic flexbox, if you put an outline on the step elements you will see the problem.

    enter image description here

    .steps-container,
    .label-container {
      display: flex;
      flex-direction: row;
      justify-content: center;
    }
    
    .step {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      outline: 1px solid red;
    }
    
    .sphere {
      display: flex;
      align-items: center;
      justify-content: center;
      width: 50px;
      height: 50px;
      border-radius: 50%;
      background-color: lightgrey;
      color: white;
      font-size: 32px;
    }
    
    .line {
      position: relative;
      top: 25px;
      height: 3px;
      background-color: lightgrey;
      flex-grow: 1;
      max-width: 150px;
    }
    
    .label {
      text-align: center;
      position: relative;
    }
    
    .spacer {
      flex-grow: 1;
      max-width: 150px;
    }
    <div class="steps-container">
      <div class="step">
        <div class="sphere">1</div>
        <div class="label">First step label</div>
      </div>
      <div class="line">&nbsp;</div>
      <div class="step">
        <div class="sphere">2</div>
        <div class="label">Second step label</div>
      </div>
    </div>

    In addition, the label is forcing the steps to be wider than the 50px of the "sphere" as you can see in the snippet above.

    An option is to force the step to only be as wide as the sphere, which we can do by setting the width to 0 but adding a min-width of 100%.

    enter image description here

    .steps-container,
    .label-container {
      display: flex;
      flex-direction: row;
      justify-content: center;
    }
    
    .step {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      outline: 1px solid red;
    }
    
    .sphere {
      display: flex;
      align-items: center;
      justify-content: center;
      width: 50px;
      height: 50px;
      border-radius: 50%;
      background-color: lightgrey;
      color: white;
      font-size: 32px;
    }
    
    .line {
      position: relative;
      top: 25px;
      height: 3px;
      background-color: lightgrey;
      flex-grow: 1;
      max-width: 150px;
      z-index: 2
    }
    
    .label {
      text-align: center;
      position: relative;
      width: 0;
      min-width: 100%;
    }
    
    .spacer {
      flex-grow: 1;
      max-width: 150px;
    }
    <div class="steps-container">
      <div class="step">
        <div class="sphere">1</div>
        <div class="label">First step label</div>
      </div>
      <div class="line">&nbsp;</div>
      <div class="step">
        <div class="sphere">2</div>
        <div class="label">Second step label</div>
      </div>
    </div>

    This leaves us with the problem of the label which we can solve by forcing nowrap and adjusting to recenter the text with a transform.

    .steps-container,
    .label-container {
      display: flex;
      flex-direction: row;
      justify-content: center;
    }
    
    .step {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
    }
    
    .sphere {
      display: flex;
      align-items: center;
      justify-content: center;
      width: 50px;
      height: 50px;
      border-radius: 50%;
      background-color: lightgrey;
      color: white;
      font-size: 32px;
    }
    
    .line {
      position: relative;
      top: 25px;
      height: 3px;
      background-color: lightgrey;
      flex-grow: 1;
      max-width: 150px;
      z-index: 2
    }
    
    .label {
      text-align: center;
      position: relative;
      width: 0;
      min-width: 100%;
      white-space: nowrap;
      translate: -50%;
    }
    
    .spacer {
      flex-grow: 1;
      max-width: 150px;
    }
    <div class="steps-container">
      <div class="step">
        <div class="sphere">1</div>
        <div class="label">First step label</div>
      </div>
      <div class="line">&nbsp;</div>
      <div class="step">
        <div class="sphere">2</div>
        <div class="label">Second step label</div>
      </div>
    </div>