Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flexbox children shrink up to a certain point (but don't expand if they don't need to)

Tags:

html

css

flexbox

I'm a having a bit of an issue here. I have a flexbox container with children of different sizes. Based on quantity and their content children might overflow the parent.

A container with 5 children (overflowing)

What I want is the children to shrink so they try to fit in the parent container. I did that by adding shrink and overflow properties to the children. So far so good.

.container > div {
  background-color: orange;
  padding: 5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex-shrink: 1;
}

I end up with something like this:

A container with 5 shrunk children

Now I want them to shrink but up to a certain point (lets say 80px). I don't care if they end up overflowing the container but I don't want to render any smaller than 80px.

Of course, I added min-width: 80px to the children... but here is my problem. I want the children to shrink up to 80px but I don't want any of those that were smaller than 80px already (like Child1, Child4 and Child5) I don't want them to be enlarged by the min-width property (or, I want them to shrink further up to min-content)

In other words. I don't want this:

Container with shrunk children (and enlarged ones!)

I would love to have something like this:

enter image description here

I tried doing something like min-width: min(min-content, 80px) but of course, didn't work.

Here is an small codepen with the issue: https://codepen.io/claudiofpen/pen/QWELVJO

.container {
  width: 300px;
  border: 1px solid black;
  display: flex;
  flex-direction: row;
  padding: 5px;
}
.container > div {
  background-color: orange;
  padding: 5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  flex-shrink: 1;
  min-width: min-content;
}
.container > div:not(:last-child) {
  margin-right: 5px;
}

/* I don't want the following css classes, I cannot 
 tell in before hand which children are going to have 
 a larger content */
.container > div:nth-child(2), 
.container > div:nth-child(3) {
  min-width: 80px;
}
<div class="container">
  <div>Child 1</div>
  <div>Longer Child 2</div>
  <div>Longer Child 3</div>
  <div>Child 4</div>
  <div>Child 5</div>
</div>
like image 591
Claudio Avatar asked Oct 06 '20 12:10

Claudio


People also ask

Which property identifies a flex item that can grow bigger but not shrink?

The flex-grow property. The flex-grow property specifies the flex grow factor, which determines how much the flex item will grow relative to the rest of the flex items in the flex container when the positive free space is distributed.

How do I stop my flexbox from growing?

By default, the child elements of a flexbox container will stretch vertically to fill the height of the container. This can be prevented by using the align-self property on the child element that you do not want to stretch.


Video Answer


2 Answers

Temani Afif's solution solves the problem of ensuring that a text element will not shrink below the specified width unless its intrinsic width is already below that width (in which case it uses its intrinsic width as the rendered width). But it does not work unless the sum of the specified widths of all the child elements exceeds the container's width.

So I tried giving each outer elements a flex-grow parameter, so that they would grow above their specified width, if the container had room. But I also give the outer elements a maximum width set to their intrinsic maximum content width, so they would never grow beyond the actual size of the text. Thus I added the following styles to the wrapping div.

 flex: 1 1 auto;
 max-width: max-content;

With that tweak I believe it solves the entire problem. The elements expand fully if there is room in the container. As we add more elements the longer elements start to shrink. But they never shrink below their specified width, so the container overflows once all inserted elements have shrunk down to that width. But elements that started with a shorter width never flex at all.

I have added an example below.

.container {
  width: 340px;
  border: 1px solid black;
  display: flex;
  flex-direction: row;
  padding: 5px;
}

.container>div {
  background-color: orange;
  padding: 5px;
  flex: 1 1 auto;
  width: 80px;
  max-width: max-content;
}

.container>div>div {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  width: 100%;
}

.container>div:not(:last-child) {
  margin-right: 5px;
}
<h5>When the items fit they expand to their intrinsic length</h5>
<div class="container">
  <div>
    <div>Medium length</div>
  </div>
  <div>
    <div>Tiny</div>
  </div>
  <div>
    <div>Longer text element</div>
  </div>
</div>
<h5>When the container limit is reached the longer elements start shrinking</h5>
<div class="container">
  <div>
    <div>Medium length</div>
  </div>
  <div>
    <div>Tiny</div>
  </div>
  <div>
    <div>Longer text element</div>
  </div>
  <div>
    <div>Filler</div>
  </div>
</div>
<h5>Adding more elements...</h5>
<div class="container">
  <div>
    <div>Medium length</div>
  </div>
  <div>
    <div>Tiny</div>
  </div>
  <div>
    <div>Longer text element</div>
  </div>
  <div>
    <div>Filler</div>
  </div>
  <div>
    <div>Filler</div>
  </div>
</div>
<h5>When there is no room it overflows<br> The tiny element stays at its intrinsic width, but the bigger elements stop shrinking at the specified width</h5>
<div class="container">
  <div>
    <div>Medium length</div>
  </div>
  <div>
    <div>Tiny</div>
  </div>
  <div>
    <div>Longer text element</div>
  </div>
  <div>
    <div>Filler</div>
  </div>
  <div>
    <div>Filler</div>
  </div>
  <div>
    <div>Filler</div>
  </div>
</div>
like image 161
user23087 Avatar answered Nov 14 '22 05:11

user23087


With an extra wrapper you can do it:

.container {
  width: 300px;
  border: 1px solid black;
  display: flex;
  flex-direction: row;
  padding: 5px;
}

.container > div {
  background-color: orange;
  padding: 5px;
  flex-shrink: 1;
  width: 80px;
}

.container > div > div {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  width: 100%;
}

.container > div:not(:last-child) {
  margin-right: 5px;
}
<div class="container">
  <div><div>Ch 1</div></div>
  <div><div>Longer Child 2</div></div>
  <div><div>Longer Child 3</div></div>
  <div><div>Child 4</div></div>
  <div><div>Child 5</div></div>
</div>
like image 35
Temani Afif Avatar answered Nov 14 '22 05:11

Temani Afif