88

I'm trying to figure out how flexbox works (supposed to work?…) for cases like below:

.holder {
  width: 500px;
  background: lightgray;
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  flex-wrap: nowrap;
}
.v2 {
  width: 320px;
}
.child {
  display: inline-block;
  border: 1px solid black;
  padding: 30px 0px;
  text-align: center;
}
<div class="holder">
  <div class="child">At a glance</div>
  <div class="child">After coverage ends</div>
  <div class="child">Forms &amp; documents</div>
</div>
<br>
<br>
<div class="holder v2">
  <div class="child">At a glance</div>
  <div class="child">After coverage ends</div>
  <div class="child">Forms &amp; documents</div>
</div>
<br>
<br>
<div class="holder v2">
  <div class="child">At a
    <br>glance</div>
  <div class="child">After coverage
    <br>ends</div>
  <div class="child">Forms &amp;
    <br>documents</div>
</div>

JSFiddle here

The problem is that when there's enough space to fit elements, I'm getting a nice tight-fitted children, with even spacing between. (first, top div block)

However, when there's no enough space and text inside children starts wrapping, it all kinda goes in a weird direction - children are not tightly fit anymore, and even though after wrapping, there's enough space around flex children, because there are not properly fit anymore, space-around doesn't really have a chance to work as well (second div block)

However still, IF I add manual line breaks at places where the automatic line breaks occur, everything gets laid out as it "should"… (bottom, third block)

What I'd like is to always have children tightly fitted within their boxes (black borders), and whatever space is left, would be distributed evenly between them, without me having to add manual line breaks (which is not an option in my case)

Is it possible at all?…

Mo.
  • 26,306
  • 36
  • 159
  • 225
Mikhail Kornienko
  • 1,162
  • 1
  • 8
  • 12

4 Answers4

73

In CSS, the parent container doesn't know when its children wrap. Hence, it continues scaling its size oblivious to what's going on inside.

Put another way, the browser renders the container on the initial cascade. It doesn't reflow the document when a child wraps.

That's why the container doesn't shrink-wrap the narrower layout. It just continues on as if nothing wrapped, as evidenced by the reserved space on the right.

The maximum length of the horizontal white space is the length of the element(s) that the container was expecting to be there.

In the following demo, whitespace can be seen coming and going as the window is re-sized horizontally: DEMO

You'll need a JavaScript solution (see here and here)... or CSS media queries (see here).

When dealing with wrapping text, text-align: right on the container may be helpful in some cases.

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
  • 1
    Why do you say that "the parent container doesn't know when its children wrap"? Why wouldn't it know? It would make sense if this was the spec. But not that "it doesn't know". Browser developers could get the content width of a child and adjust its container width accordingly. – Cully Sep 09 '22 at 17:25
  • 2
    How does it happen that the container does not know about its content's wrap in terms of the width but does know about it when it comes to the height? (the container does change its height accordingly to fit its wrapped content) – Ilya Loskutov Nov 15 '22 at 09:09
  • @IlyaLoskutov, can you post an example? Perhaps a code sample in jsFiddle. – Michael Benjamin Nov 17 '22 at 15:01
  • 1
    @MichaelBenjamin [here](https://stackoverflow.com/questions/74431243) is my closed question on the matter in hand. `.item` acts as the container for text. When the text wraps, the container changes its height to fit it (the difference can be observed in the two pictures I've attached to) but, indeed, it don't want to shrink its width to amount to the longest line of the wrapped text. The same can be seen in [this example](https://codepen.io/Ilya-Loskutov/pen/poKWXwV) with flex box (`.internal-container` changes its height as its items wrap) – Ilya Loskutov Nov 17 '22 at 16:33
  • It's a good observation. Consider posting a complete question. Ping me, if you want. – Michael Benjamin Nov 17 '22 at 16:55
  • 1
    The answer basically is that CSS sucks and has many limitations. – GeForce RTX 4090 Feb 06 '23 at 13:42
5

Have a good look at my Fiddle in which I changed:

  • .holder width to max-width (in .v classes)
  • modified .holder to wrap and space-around its children
  • added 2 more .v classes for clarity
  • removed the <br>'s
  • and, most importantly, added flex: 0 0 to .child

Flexbox almost always needs max-width to be set, which is more flexible than width. Depending on how you need the .children to behave, modify the flex-grow and flex-shrink in flex: 0 0 to meet your needs. (the result of flex: 1 0 looks nice too)

...no Javascript needed...

The Codrops Flexbox Reference is very useful to understand Flexible Box Layout.

PeterJames
  • 1,137
  • 1
  • 9
  • 17
Rene van der Lende
  • 4,992
  • 2
  • 14
  • 25
  • 3
    for anybody wondering, `flex: 0 0;` does not achieve OP's requirements; it forces flex children to wrap their text even when there is enough space for it. Compare the top flex parent in the two fiddles, where there is ample horizontal space but every element's text still wraps. – Bashu Naimi-Roy Nov 03 '21 at 20:26
2

The reason why your blocks behave like this is because of CSS rendering.

In the first case in you fiddle the browsers doesn't know when the block gets too small for it's content. So it keeps stretching until it reaches the maximum and then renders the text.

In your last case you tell the browser where to break so it knows that the element should not get wider.

The only way you can easily solve this is by setting the breaks yourself.

Wart Claes
  • 223
  • 1
  • 11
  • 1
    Thanks! I was afraid something like that might be happening… Still hoping some smart hacks exist, since it looks like a rather common situation… – Mikhail Kornienko May 24 '16 at 07:19
  • 2
    We had a similar issue at our work where we wanted an icon to float right next to the text: http://codepen.io/WartClaes/pen/grVQrz?editors=1100. Only we bumped also into the issue that the block always renders the full width nevertheless there was a lot of whitespacing. – Wart Claes May 24 '16 at 07:26
0

If you're looking for a CSS-only solution, you can use something like width: 100px; to set the minimum width of the child and flex: auto; so that it grows.

Example: https://jsfiddle.net/ncvp7gzs/1

(Note this is only tested this on Chrome)

Mark Chapel
  • 491
  • 6
  • 11