Home > Enterprise >  Equal padding for flexbox items that include word-wrapped text
Equal padding for flexbox items that include word-wrapped text

Time:11-29

I've got a fixed-width container element that contains several variable-width child elements. I'd like to distribute extra space evenly between those elements. This is easy to do if none of the elements include word-wrapped text. But if the total content is wider than will fit in the container without wrapping, I'm not sure how to distribute space between elements anymore.

Here's a repro showing it working great when there's no wrapping, but not if there's wrapped text: enter image description here

.container {
  display: flex;
  flex-direction: column;
}

.bad {
  width: 600px;
}

.good {
  width: 800px;
}

ul {
  display: flex;
  flex-grow: 1;
  width: 100%;
  gap: 20px;
  padding: 20px;
  border: 1px solid gray;
  list-style: none;
}

li {
  flex-grow: 1;
  flex-basis: auto;
  padding: 10px 0;
  border: 1px solid red;
  text-align: center;
}

ul.basis li {
  flex-basis: 1px;
}
<div class='container good'>
  <label>Works as expected (consistent padding) if nothing wraps</label>
  <ul>
    <li>South Carolina</li>
    <li>North Carolina</li>
    <li>Virginia</li>
    <li>Alaska</li>
    <li>District of Columbia</li>
    <li>California</li>
    <li>Arizona</li>
  </ul>
</div>

<div >
  <label>Not desired (padding varies) using <code>flex-basis: auto</code></label>
  <ul>
    <li>South Carolina</li>
    <li>North Carolina</li>
    <li>Virginia</li>
    <li>Alaska</li>
    <li>District of Columbia</li>
    <li>California</li>
    <li>Arizona</li>
  </ul>
</div>

<div >
  <label>Not desired (padding varies) using <code>flex-basis: 1px</code></label>
  <ul >
    <li>South Carolina</li>
    <li>North Carolina</li>
    <li>Virginia</li>
    <li>Alaska</li>
    <li>District of Columbia</li>
    <li>California</li>
    <li>Arizona</li>
  </ul>
</div>

CodePudding user response:

I guess in your .bad cases the problem comes from the restricted width of your flex box. The list items already have the maximum space they can get and so they CAN'T grow any padding.

You can see the problem if you add some padding yourself to the list items.
I'm adding 10px of padding left and right padding: 10px 10px; to your examples <li> elements. This way it WOULD have consistent padding for all lis titems, but the items are growing to large and are sticking outside of the flex box.

.container {
  display: flex;
  flex-direction: column;
}
.bad {
  width: 600px;
}
.good {
  width: 800px;
}
ul {
  display: flex;
  flex-grow: 1;
  width: 100%;
  gap: 20px;
  padding: 20px;
  border: 1px solid gray;
  list-style: none;
}
li {
  flex-grow: 1;
  flex-basis: auto;
  padding: 10px 10px;
  border: 1px solid red;
  text-align: center;
}
ul.basis li {
  flex-basis: 1px;
}
<div class='container good'>
  <label>Works as expected (consistent padding) if nothing wraps</label>
  <ul>
    <li>South Carolina</li>
    <li>North Carolina</li>
    <li>Virginia</li>
    <li>Alaska</li>
    <li>District of Columbia</li>
    <li>California</li>
    <li>Arizona</li>
  </ul> 
</div>

<div >
  <label>Not desired (padding varies) using <code>flex-basis: auto</code></label>
  <ul>
    <li>South Carolina</li>
    <li>North Carolina</li>
    <li>Virginia</li>
    <li>Alaska</li>
    <li>District of Columbia</li>
    <li>California</li>
    <li>Arizona</li>
  </ul>
 </div>

<div >
  <label>Not desired (padding varies) using <code>flex-basis: 1px</code></label>
  <ul >
    <li>South Carolina</li>
    <li>North Carolina</li>
    <li>Virginia</li>
    <li>Alaska</li>
    <li>District of Columbia</li>
    <li>California</li>
    <li>Arizona</li>
  </ul>
</div>

So I only see two options in order to have equal paddings for all items.

Case one, let the words break anywhere:
You can add the 10px left and right padding as in above example and in addition, also add word-break: break-all; This way the items will not grow larger as they can, have equal paddings BUT it's hard to read because you allow words to break at any position.

.container {
  display: flex;
  flex-direction: column;
}
.bad {
  width: 600px;
}
.good {
  width: 800px;
}
ul {
  display: flex;
  flex-grow: 1;
  width: 100%;
  gap: 20px;
  padding: 20px;
  border: 1px solid gray;
  list-style: none;
}
li {
  flex-grow: 1;
  flex-basis: auto;
  padding: 10px 10px;
  border: 1px solid red;
  text-align: center;
  word-break: break-all;
}
ul.basis li {
  flex-basis: 1px;
}
<div class='container good'>
  <label>Works as expected (consistent padding) if nothing wraps</label>
  <ul>
    <li>South Carolina</li>
    <li>North Carolina</li>
    <li>Virginia</li>
    <li>Alaska</li>
    <li>District of Columbia</li>
    <li>California</li>
    <li>Arizona</li>
  </ul> 
</div>

<div >
  <label>Not desired (padding varies) using <code>flex-basis: auto</code></label>
  <ul>
    <li>South Carolina</li>
    <li>North Carolina</li>
    <li>Virginia</li>
    <li>Alaska</li>
    <li>District of Columbia</li>
    <li>California</li>
    <li>Arizona</li>
  </ul>
 </div>

<div >
  <label>Not desired (padding varies) using <code>flex-basis: 1px</code></label>
  <ul >
    <li>South Carolina</li>
    <li>North Carolina</li>
    <li>Virginia</li>
    <li>Alaska</li>
    <li>District of Columbia</li>
    <li>California</li>
    <li>Arizona</li>
  </ul>
</div>

Or case two, let the flex box wrap it's items:
Again, add the 10px left and right. Also, instead of breaking the text at any random position, you can let the items itself wrap down into the next row with flex-wrap: wrap; on your <ul> element.
You probably also want to remove the flex-grow: 1; from your <li> elements, or some items grow to the full flex box width.

.container {
  display: flex;
  flex-direction: column;
}
.bad {
  width: 600px;
}
.good {
  width: 800px;
}
ul {
  display: flex;
  flex-wrap: wrap;
  flex-grow: 1;
  width: 100%;
  gap: 20px;
  padding: 20px;
  border: 1px solid gray;
  list-style: none;
}
li {
  flex-basis: auto;
  padding: 10px 10px;
  border: 1px solid red;
  text-align: center;
}
ul.basis li {
  flex-basis: 1px;
}
<div class='container good'>
  <label>Works as expected (consistent padding) if nothing wraps</label>
  <ul>
    <li>South Carolina</li>
    <li>North Carolina</li>
    <li>Virginia</li>
    <li>Alaska</li>
    <li>District of Columbia</li>
    <li>California</li>
    <li>Arizona</li>
  </ul> 
</div>

<div >
  <label>Not desired (padding varies) using <code>flex-basis: auto</code></label>
  <ul>
    <li>South Carolina</li>
    <li>North Carolina</li>
    <li>Virginia</li>
    <li>Alaska</li>
    <li>District of Columbia</li>
    <li>California</li>
    <li>Arizona</li>
  </ul>
 </div>

<div >
  <label>Not desired (padding varies) using <code>flex-basis: 1px</code></label>
  <ul >
    <li>South Carolina</li>
    <li>North Carolina</li>
    <li>Virginia</li>
    <li>Alaska</li>
    <li>District of Columbia</li>
    <li>California</li>
    <li>Arizona</li>
  </ul>
</div>



And just to wrap it up again, I wouldn't be aware of any method to not do either of them (breaking words anywhere or wrapping items to next row) AND keep the items inside your flex-box.
Of course you could always play around with your gap and other properties as well in order to fit your items into a specific container width. But as soon as you have more items in your container (for example they are not hardcoded but pulled from a database), you will always hit a limit where it's no longer possible to display your items in an uniform way.

CodePudding user response:

Still WAY too much CSS here but you can decide how you wish the list text to wrap internally and which is visually most appealing to you. First block as you had it, the other two I wrapped in a container to better control the flow IMHO. Basic concept is to actually say what padding you DO want.

.container {
  display: flex;
  flex-direction: column;
}

.bad {
  width: 400px;
}

.good {
  width: 800px;
}

.list-container {
  width: 100%;
}

.list-container>.list-group {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  list-style: none;
  align-items: center;
  justify-content: space-around;
}

.list-container>.list-group>* {
  padding: 0.25rem;
}

ul {
  display: flex;
  flex-grow: 1;
  gap: 1rem;
  padding: 20px;
  border: 1px solid gray;
  list-style: none;
}

li {
  flex-grow: 1;
  flex-basis: auto;
  padding: 10px 0;
  border: 1px solid red;
  text-align: center;
}

ul.basis li {
  flex-basis: 1px;
}
<div class='container good'>
  <label>Works as expected (consistent padding) if nothing wraps</label>
  <ul>
    <li>South Carolina</li>
    <li>North Carolina</li>
    <li>Virginia</li>
    <li>Alaska</li>
    <li>District of Columbia</li>
    <li>California</li>
    <li>Arizona</li>
  </ul>
</div>

<div >
  <label>Not wrapped text!</label>
  <div >
    <ul >
      <li>South Carolina</li>
      <li>North Carolina</li>
      <li>Virginia</li>
      <li>Alaska</li>
      <li>District of Columbia</li>
      <li>California</li>
      <li>Arizona</li>
    </ul>
  </div>
</div>

<div >
  <label>Wrapped text <code>flex-basis: 1px</code></label>
  <div >
    <ul >
      <li>South Carolina</li>
      <li>North Carolina</li>
      <li>Virginia</li>
      <li>Alaska</li>
      <li>District of Columbia</li>
      <li>California</li>
      <li>Arizona</li>
    </ul>
  </div>
</div>

  • Related