Why don't flex items shrink past content size?
Asked Answered
B

7

568

I have 4 flexbox columns and everything works fine, but when I add some text to a column and set it to a big font size, it is making the column wider than it should be due to the flex property.

I tried to use word-break: break-word and it helped, but still when I resize the column to a very small width, letters in the text are broken into multiple lines (one letter per line), and yet the column does not get smaller width than one letter size.

Watch this video (at the start, the first column is the smallest, but when I resized the window, it is the widest column. I just want to respect flex settings always; flex sizes 1 : 3 : 4 : 4)

I know, setting font-size and column padding to smaller will help... but is there any other solution?

I can not use overflow-x: hidden.

JSFiddle

.container {
  display: flex;
  width: 100%
}
.col {
  min-height: 200px;
  padding: 30px;
  word-break: break-word
}
.col1 {
  flex: 1;
  background: orange;
  font-size: 80px
}
.col2 {
  flex: 3;
  background: yellow
}
.col3 {
  flex: 4;
  background: skyblue
}
.col4 {
  flex: 4;
  background: red
}
<div class="container">
  <div class="col col1">Lorem ipsum dolor</div>
  <div class="col col2">Lorem ipsum dolor</div>
  <div class="col col3">Lorem ipsum dolor</div>
  <div class="col col4">Lorem ipsum dolor</div>
</div>
Beria answered 27/3, 2016 at 12:8 Comment(0)
L
1039

The Automatic Minimum Size of Flex Items

You're encountering a flexbox default setting.

A flex item cannot be smaller than the size of its content along the main axis.

The defaults are...

  • min-width: auto
  • min-height: auto

...for flex items in row-direction and column-direction, respectively.

You can override these defaults by setting flex items to:

  • min-width: 0
  • min-height: 0
  • overflow: hidden (or any other value, except visible)

Flexbox Specification

4.5. Automatic Minimum Size of Flex Items

To provide a more reasonable default minimum size for flex items, this specification introduces a new auto value as the initial value of the min-width and min-height properties defined in CSS 2.1.

With regard to the auto value...

On a flex item whose overflow is visible in the main axis, when specified on the flex item’s main-axis min-size property, specifies an automatic minimum size. It otherwise computes to 0.

In other words:

  • The min-width: auto and min-height: auto defaults apply only when overflow is visible.
  • If the overflow value is not visible, the value of the min-size property is 0.
  • Hence, overflow: hidden can be a substitute for min-width: 0 and min-height: 0.

and...


You've applied min-width: 0 and the item still doesn't shrink?

Nested Flex Containers

If you're dealing with flex items on multiple levels of the HTML structure, it may be necessary to override the default min-width: auto / min-height: auto on items at higher levels.

Basically, a higher level flex item with min-width: auto can prevent shrinking on items nested below with min-width: 0.

Examples:


Browser Rendering Notes

  • Chrome vs. Firefox / Edge

    Since at least 2017, it appears that Chrome is either (1) reverting back to the min-width: 0 / min-height: 0 defaults, or (2) automatically applying the 0 defaults in certain situations based on a mystery algorithm. (This could be what they call an intervention.) As a result, many people are seeing their layout (especially desired scrollbars) work as expected in Chrome, but not in Firefox / Edge. This issue is covered in more detail here: flex-shrink discrepancy between Firefox and Chrome

  • IE11

    As noted in the spec, the auto value for the min-width and min-height properties is "new". This means that some browsers may still render a 0 value by default, because they implemented flex layout before the value was updated and because 0 is the initial value for min-width and min-height in CSS 2.1. One such browser is IE11. Other browsers have updated to the newer auto value as defined in the flexbox spec.


Revised Demo

.container {
  display: flex;
}

.col {
  min-height: 200px;
  padding: 30px;
  word-break: break-word
}

.col1 {
  flex: 1;
  background: orange;
  font-size: 80px;
  min-width: 0;   /* NEW */
}

.col2 {
  flex: 3;
  background: yellow
}

.col3 {
  flex: 4;
  background: skyblue
}

.col4 {
  flex: 4;
  background: red
}
<div class="container">
  <div class="col col1">Lorem ipsum dolor</div>
  <div class="col col2">Lorem ipsum dolor</div>
  <div class="col col3">Lorem ipsum dolor</div>
  <div class="col col4">Lorem ipsum dolor</div>
</div>

jsFiddle

Lett answered 27/3, 2016 at 12:41 Comment(10)
Looks like Chrome changed the behavior in v73 to not shrink smaller than the content.Ionic
Same fix worked with min-height: 0; . I had to try setting this for several higher elements in the tree.Mcnelly
I had to set min-height: 0; in ike 10 places to get it to work. Thank you, thank you, thank you for the great tip!Wivern
@dave0688, maybe an overall reset would be simpler: * { min-height: 0; }Lett
@Ionic it would be against the purpose of a browser to hide, destroy, distort or otherwise show in an incomplete manner the original informationRefrain
I still don't know when we should add min-width:0, for the example above, resize the window to small and the .col1 has a width of 0, but we can still see the character, can someone provide a example that makes more sense?Likker
You simply solved my problem just by describing the nesting flex shrinking behaviour. THANK YOUShreeves
For me the need to set min-width: 0 was only necessary in Chrome. In Firefox it all worked fine.Punt
What if, a child element is not intended to shrink below 400px height, and doesn't once min-height is set to 400px, but since the parents has been set a min-height of 0, the parents still shrink resulting in some sort over overflow of the child ? how could you solve this if you've set a lot of "min-height: 0px" on parent containers ? is there some way to bubble up the child min-height constraint to the parents ?Haase
@AxelSamyn, this should be posted as a full question, with code that reproduces the scenario.Lett
S
29

I'm finding this has bitten me repeatedly over the years for both flex and grid, so I'm going to suggest the following:

* { min-width: 0; min-height: 0; }

and then just use min-width: auto or min-height: auto if you need that behaviour.

In fact, throw in box-sizing as well to make all layout more sane:

* { box-sizing: border-box; min-width: 0; min-height: 0; }

Does anyone know if there are any odd consequences? I've not encountered anything in several years of using a mix of the above. In fact, I can't think of any cases where I'd want to layout from content outwards to the flex/grid, rather than flex/grid inwards to the content --- and surely if they exist, they're rare. So this feels like a bad default. But maybe I'm missing something?

Swick answered 24/8, 2020 at 8:40 Comment(6)
yes, the side effect is the shadow dom and slotted elements, which now required !important if you want to change this default min-width.Hammond
Btw, for the web component case, the trick is to use css var. This way, you have default value, but can be overridden, while still have the * { mid-width: ..} above. :host{ --min-width: 10rem; min-width: var(--min-width) !important; ... } and then, in app.css you can have c-menu{ --min-width: 30rem; }Hammond
Your suggested default settings (0) used to be the flex defaults (consistent with the rest of CSS). There are reasons for modifying the spec from 0 to auto. You may want to consider these reasons before switching them back. If you're interested in the details, start here: w3.org/TR/css-flexbox-1/#min-size-autoLett
@MichaelBenjamin, Curiously, I'd say the spec provides better reasons to avoid content-based minimums. Nevertheless, the main point they make in its favour is that it is "often appropriate, and helps prevent content from overlapping or spilling outside its container" --- but is that needed more often than outside-in layout? My own experience has suggested otherwise and `min-<dim>: 0' hasn't caused me any issues yet, but I acknowledge the (practical) issue with web components @JeremyChone mentions. If you had more links on the reasoning, would be happy to check them out.Swick
@Swick - This post currently has nearly 64K views and the accepted answer is closing in on 600 upvotes. Even closed duplicates of this question (such as this one and this one) have tens of thousands of views and hundreds of upvotes. So, yeah, this issue may need to be revisited by spec authors.Lett
For the record, these default settings apply in Grid layout, as well. w3.org/TR/css3-grid-layout/#min-size-autoLett
R
2

For this code below, adding width: 100% solved my problem.

.post-cover .inner {
    display: flex;
    flex-direction: column;
    flex-wrap: wrap;
    justify-content: center;
    align-content: flex-start;
    align-items: flex-start;
    word-break: break-all;
    z-index: 21;
}
.post-cover .article-page {
    padding: 20px 0;
    margin-bottom: 40px;
    font-size: 0.875em;
    line-height: 2.0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    width: 100%;   /* Add this */
}
Ripieno answered 22/4, 2021 at 23:25 Comment(0)
R
2

The pure answer to your question is that by default, browsers tend to display as much information as possible to the reader (and not to hide anything).

That happens by default, and even includes showing default black color fonts on a white background (for maximum page contrast and readability), adding a scroll bar where content is larger than the viewport height (or width) or still showing content from a markup (or the background color) even if this was mistakenly placed after </body> or even </html> tags in the html file.

In context of CSS, this applies as well, but you also are allowed to play with many customizations on top of that.

Even in a screen if using a huge font (like font-size: 50em;) this initially acts as an overflowing element (and placing the font inside a flexible child container by using display: flex doesn't change this default behaviour unless you use overflow: hidden or resize the element in some way.

An elegant solution is to use a dynamic resizing of the letters, for example

font-size: calc(0.5em + 2vw)

which works great even in a responsive scenario.

As a previous answer mentioned, A flex item cannot be smaller than the size of its content along the main axis (for the same reason, that is not only specific to the flexbox model implemented in CSS but because of the inner browser way of working). Even a long word is displayed with a scrollbar if it's longer than display width as if being a block type element with a fixed size instead.

This is mentioned in old html 4.01 specifications as

"By convention, visual HTML user agents wrap text lines to fit within the available margins. Wrapping algorithms depend on the script being formatted.

In Western scripts, for example, text should only be wrapped at white space. "

as seen here in paragraph 9.5.3. This means that, since then, the text had to be continuously displayed by default (unless we decide to split it but not at single character level: a single non-white character shown at 120em size will trigger scrollbars displaying on the browser).

Words are also clearly defined in paragraph 9.1 in the same source:

we use the term "word" here to mean "sequences of non-white space characters"

The purpose of displaying the original format of any word is to not destroy, hide or distort the original information, the meaning or intent of the code author. As such, we also have &nbsp; for keeping in same line two words that are connected - when breaking them might be disruptive (such as New York, 10 PM, 10 km/h, § 10, etc)

Refrain answered 19/4, 2022 at 20:6 Comment(0)
B
-1

What about:

.parent {
  display: flex;
  width: 420px;
}

.child-with-ellipses {
  flex-basis: auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.child-without-ellipses {
  flex-basis: 69%;
  background-color: red;
}
<div class="parent">
  <div class="child-with-ellipses">
    This node has the class `child-with-ellipses`.
  </div>
  <div class="child-without-ellipses"></div>
</div>
Bet answered 9/8, 2023 at 11:45 Comment(0)
N
-1

The most efficient solution as for now is using -webkit-line-clamp: https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-line-clamp

You can specify number of lines to preserve, for example 1.

Old solution based on min-width: 0 does not work on complex layouts with wide usage of flex.

Nonconformist answered 11/8, 2023 at 0:17 Comment(0)
V
-3

I tried everything, even putting the below code in the index.css.

* {
  min-width: 0;
  min-height: 0;
  box-sizing: border-box;
}

But nothing worked.

finally I made the div I wanted to shrink past it's content to have position: absolute;. Then it started shrinking. It's parent div would need a defined height and width. This might not be the best solution for every scenario but if this works for you, good!

Velamen answered 30/5, 2022 at 0:18 Comment(1)
answer has nothing to do with flex items shrinking, as now the items are not flex itemsHesperides

© 2022 - 2024 — McMap. All rights reserved.