How to prevent a flex item from shrinking smaller than its content? - html

I've set up a jsfiddle to demonstrate the problem here:
http://jsfiddle.net/x0eo3aeo/2/
HTML:
<div class="flexContainer">
<div class="flexCol1">aaa</div>
<div class="flexCol2"><div style="width:100px; background-color:yellow;">bbb</div></div>
<div class="flexCol3"><div style="width:250px; background-color:pink;">Hello world, some long text here to make this element stay at 250px while splitting onto multiple lines.</div></div>
</div>
CSS:
.flexContainer {
display: flex;
width: 100%;
flex-direction: row;
}
.flexCol1, .flexCol3 {
flex: 1;
background-color: green;
}
Firefox actually behaves exactly how I want. Columns 1 and 3 flex equally until the width of column 3 hits the fixed size of its child div, and then only column 1 is flexed. However, in Chrome, both columns continue to flex equally and the child content of column 3 overflows.
Is there a way to achieve the Firefox-style behaviour in a cross-browser way?

You should be able to achieve the Firefox behavior in Chrome by adding min-width: -webkit-min-content; to .flexCol3. This prevents it from shrinking below its min-content width. (This is what's supposed to happen by default, due to min-width:auto introduced in the flexbox spec, but that hasn't been implemented in Chrome yet.)
As noted in comments below, IE doesn't seem to have a min-content width keyword implemented, so you might have to do something hackier there (like min-width: 250px). Fortunately, IE's next release (12?) does have min-width:auto implemented, so this should Just Work like Firefox there, I'm told.

Related

How to make both an image and a subtitle div automatically resize and fit the screen properly in both Firefox and Chrome? [duplicate]

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>
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...
The minimum sizing algorithm applies only on the main axis.
For example, a flex item in a row-direction container does not get min-height: auto by default.
For a more detailed explanation see this post:
min-width rendering differently in flex-direction: row and flex-direction: column
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:
Flex item is not shrinking smaller than its content
Fitting child into parent
white-space css property is creating issues with flex
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
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?
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 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)
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 */
}
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!

Flex image won't shrink with screen size when container is a hyperlink

Can someone let me know if this is an undocumented bug with flexbox, or that I'm just doing it wrong? I've got 3 images lined up in a row inside a div container. This is as simple as it gets folks.
Without any hyperlinks, all 3 images shrink down perfectly as they should.
<div style="width: 100%; margin: 0 auto; display: flex; justify-content: center;">
<img src="flash-tooltip.png">
<img src="html-tooltip.png">
<img src="portables-tooltip.png">
</div>
Now, only 2 out of the 3 images when viewed on all devices shrink down correctly depending on manually maximizing dragging the browser, of via viewport.
The only image that will not change shape or size is the image with the hyperlink. So, I took the hyperlink off the first image. And decided to test it by placing it on the 2nd, now the 1st image and the 3rd image shrinks fine.
But, the 2nd image stays the exact same size? Tried then adding hyperlinks to all the images and none of them change to match the screen width?
Am I wrong to say flex items if they are images won't flex if they have a hyperlink lol? Surely this cannot be the case right?
The problem has nothing to do with hyperlinks. You could wrap the image in any element (try a span or a div) and it will have the same effect as the a container.
The problem is the hierarchical structure of a flex container.
When you set an element to display: flex (or inline-flex) you establish a flex container.
Only the children of a flex container are flex items. Descendants of a flex container beyond the children are not flex items and don't accept flex properties.
Here are the three flex items:
<img src="flash-tooltip.png">
<img src="html-tooltip.png">
<img src="portables-tooltip.png">
The img in the first element is not a flex item. It is wrapped by the a element and is therefore a child of a flex item.
The two img items can shrink because of two default settings on a flex container:
flex-wrap: nowrap ~ flex items are forced to remain on a single line
flex-shrink: 1 ~ flex items are permitted to shrink to prevent them from overflowing the container
If you switch to flex-wrap: wrap and/or flex-shrink: 0 the img items will no longer shrink.
The a item does not shrink because of another default setting: min-width: auto, which means that flex items cannot be smaller than the size of their content. In this case, the a item cannot shrink below the width of the image.
You can override this setting by adding min-width: 0 to your code.
#container {
display: flex;
}
span {
min-width: 0;
display: flex;
}
img {
min-width: 0;
}
<div id="container">
<span><img src="http://i.imgur.com/60PVLis.png"></span>
<img src="http://i.imgur.com/60PVLis.png">
<img src="http://i.imgur.com/60PVLis.png">
</div>
More information:
Why don't flex items shrink past content size?
Proper use of flex properties when nesting flex containers
I don't know why, but this solves the problem. I would like to know why as I cannot find out any information about this issue in any HTML/CSS documents.
If you add the following.
<style>
img {
max-width: 100%;
height: auto;
}
</style>
Then all 3 images will shink perfectly. Even if they have hyperlinks. Funny enough if you set just the width: 100%; then the image with the hyperlink stays the exact same size as the image is, and all the others without hyperlinks blow up to the 100% size of the container.
I didn't know flexbox had such rules that needed you to set image max-widths to make items responsive/shrink down if they have a hyperlink attached.
So, tried it in chrome: Only the image now with the anchor shrinks down, the other 2 stay the same size. FireFox all 3 shrink down, but chrome only shrinks the image with the hyperlink wrapped around it.
Tried wrapping hyperlinks around each of the other 2 images and in chrome, they all shrink down fine.
Can someone explain what is going on? How can i set a max-width: and height: auto on a hyperlink?
It's tough to say without seeing your CSS, but you probably are not selecting the images within <a> tags. If you alter your CSS to select images that are inside of <a> tags, it should work fine.
I've added my complete working solution. Thanks to many people here giving their various methods. So, this is for anyone else who may be struggling.
First lets set the style's up.
<style>
img {
max-width: 100%;
width: 100%;
min-width: 0;
min-height: 0;
}
</style>
adding min-width: 0; | min-height: 0; seems to be overkill, but with chrome, it works much better apparently than setting them as auto;
Since it's using flexbox we don't add the usual width: 33.33%; even if there are 3 images. In flexbox, this will just space them out way to far apart within a 100% wide div.
Here's the really important part I found out the hard way.
You must use either width: 100% on the images, or max-width: 100%; otherwise, (On Chrome without adding either 100% width or max-width: 100%; it just won't flex/shrink down when you minimize the browser to test its responsiveness.)
So, next to keep each of the 3 images in perfect aspect ratio remember to include each image inside its own div container. Otherwise, they will shrink but will just skew up to each other as they do.
As you can see the first image is even wrapped in a hyperlink, but because it's inside its own div it will shrink and grow completely flush and inline with the other images. This saves using extra markup and saves adding a span tag then making that a flex container to contain the hyperlink. I've tried both ways this is by far the easier method.
I've used inline styles for the flexbox container. (bad habit.)
<div style="border: 2px solid red; margin: 0 auto; display: flex; justify-content: center;">
<div>
<img src="flash-tooltip.png">
</div>
<div>
<img src="html-tooltip.png">
</div>
<div>
<img src="portables-tooltip.png">
</div>
</div>
Remember to close off that last /div it's a real gotcha!
And that's how I've done it. Tested it in many browsers works perfectly. Even on mobile phones and tablets.
If you don't like flexbox? You can do the same thing using regular floats.
I've included this same method as above, only this time in a float: version.

Display: Flex not working as expected in Internet Explorer [duplicate]

As far as I've been able to gather, if working with IE10 / IE11 I should be able to use the standardized flex terms.
I have a container div and 2 child divs.
The 2 child divs are not larger than 400px, so there should always be enough room for the justify-content: space-between.
I want the first child to be all the way at the top and the second child to be all the way at the bottom.
This works in Chrome and Firefox but not in IE, and I have no idea why.
Any comments and feedback are welcome.
<div style="display: flex; flex-direction: column; justify-content: space-between; min-height: 400px; background-color: lightyellow;">
<div style="background-color: red;">
<h2>Title (variable height)</h2>
<p>Summary (variable height)</p>
</div>
<div style="background-color: orange;">
<img src="http://avatarbox.net/avatars/img32/tv_test_card_avatar_picture_61484.jpg" />
</div>
</div>
https://jsfiddle.net/akxn68vm/
IE 10 & 11 have a number of issues with rendering flexbox properly.
Here's one: A flex container doesn't respect the min-height property in these browsers.
A simple solution is to make your flex container also a flex item.
Just add this to your code (no other changes necessary):
body {
display: flex;
flex-direction: column;
}
revised fiddle
More info: https://github.com/philipwalton/flexbugs#flexbug-3
According to this bug IE11 doesn't render the items correctly when using min-height in flexbox.
It seems like the problem was solved in Edge, but IE10-11 will not work.
This is a bug in IE10/11. You can find the information at https://github.com/philipwalton/flexbugs#flexbug-3
To fix this bug in IE10/11, add a wrapper element around the flex container that is itself a flex container. In your example, you can add display flex to body tag. And add width 100% style in the container div

Flex-shrink not working as expected

I'm starting to work with flexbox, and, in order to understand flex-grow and flex-shrink, I used a simple program that displays two blocks and makes them take up the whole width using flex-grow: 2 in one of them and flex-grow: 1 in the other.
If I check the following line in the console: $(".one").width() === $(window).width() /3 it returns true. So far, so good.
The problem appears when I reduce the window size, because as soon as I do this the same line in the console ($(".one").width() === $(window).width() /3) starts returning false.
I know the default value for flex-shrink is 1. Wouldn't that mean that the proportions between both blocks would be maintained (since they are both being shrunk by the same amount)? Can anyone explain why this result happens?
Here's my code:
* {
font-family: verdana;
margin: 0;
}
body {
background: #eee;
}
.wrapper {
width: 100%;
max-width: 2000px;
margin: 0 auto;
}
.flex-container {
display: flex;
background-color: white;
}
.box {
height: 100px;
}
.one {
background-color: red;
flex-grow: 1;
}
.two {
background-color: blue;
flex-grow: 2;
}
<div class="wrapper">
<div class="flex-container">
<div class="box one"></div>
<div class="box two"></div>
</div>
</div>
While not relevant to your question it might be worth noting:
flex-wrap takes precedence over flex-shrink, unless the item is wider than the container.
So if you have flex-wrap enabled and you're expecting two items to shrink to fit side by side on a single row they won't, they'll wrap first even if there's plenty of shrink-potential.
If the item is wider than the parent container it can't wrap so it will shrink if it can.
You'd have to solve this with min/max widths, make the initial size smaller (this is probably the best way) or creating a new parent container without flex-wrap.
See also Is there any use for flex-shrink when flex-wrap is wrap?
flex-shrink is designed to distribute negative free space in the container.
In other words, it only works when flex items are big enough to overflow the container.
You're not having that problem here. There is no negative space. Therefore, I don't believe flex-shrink is having any effect on your layout.
flex-grow is consuming the positive free space and seems to be working fine.
You would need to set a flex-basis or add content to the items to put flex-shrink in play.
https://www.w3.org/TR/css-flexbox-1/#flex-property
This is related to float calculations. Your flex code is working perfectly fine, the problem arises from the arithmetic operation, where the width of the container might not perfectly divide to 3, so the result will be a floating number which might or not be rounded to the closest number, so that's why width of your first flexbox item might not be equal to width / 3 because of that rounding.
Tested with Chrome Inspector.
Take a look at https://css-tricks.com/snippets/css/a-guide-to-flexbox/#article-header-id-13
They suggest using the shorthand flex: [number]; because it intelligently sets the default flex-shrink to 0. Just because the default for flex-shrink is 1 doesn't mean that 1 is what you want. I haven't been using flexbox that long, but I've yet to come across a scenario in which I've had to specify a flex-shrink. 0 has been working for me thus far. Maybe somebody else can provide a scenario for using it.
TLDR
Use flex attribute instead of flex-grow

How can I make a display:flex container expand horizontally with its wrapped contents?

When using css flexbox the three main browsers appear to behave entirely differently in certain areas.
In this case I am trying to create a grid of images:
<div class="container">
<div class="photo"></div>
<div class="photo"></div>
<div class="photo"></div>
<div class="photo"></div>
<div class="photo"></div>
<div class="photo"></div>
</div>
.container {
display:inline-flex;
flex-flow : column wrap;
align-content : flex-start;
height : 100%;
}
In this example I need a container, itself containing several div elements set up to flow from top to bottom and wrapping when they reach the bottom. Ultimately providing me with columns of photos.
However I need the container to expand horizontally to accommodate the wrapped elements:
Here is a quick jsFiddle to demonstrate.
The behaviour is as follows:
IE 11 - Correct, the container stretches horizontally to wrap each column of wrapped elements
Firefox - The container only wraps the first column of elements, with the rest overflow out.
Chrome - The container always stretches to fill the width of its parent, whatever that may be.
In this instance I would like to achieve the behaviour of IE11 in the other two browsers. Therefore my question is, how can I make a flexbox container expand horizontally to match its column wrap contents.
Thanks in advance.
It's curious that most browsers haven't implemented column flex containers correctly, but the support for writing modes is reasonably good.
Therefore, you can use a row flex container with a vertical writing mode. This will swap the block direction with the inline direction, and thus the flex items will flow vertically. Then you only need to restore the horizontal writing mode inside the flex items.
.container {
display: inline-flex;
writing-mode: vertical-lr;
flex-wrap: wrap;
align-content: flex-start;
height: 350px;
background: blue;
}
.photo {
writing-mode: horizontal-tb;
width: 150px;
height: 100px;
background: red;
margin: 2px;
}
<div class="container">
<div class="photo">1</div>
<div class="photo">2</div>
<div class="photo">3</div>
<div class="photo">4</div>
<div class="photo">5</div>
<div class="photo">6</div>
<div class="photo">7</div>
<div class="photo">8</div>
<div class="photo">9</div>
</div>
This approach may have its own bugs in edge cases, especially if you mix advanced layout techniques like floats and nested flexboxs. But for most cases it seems to work properly.
The spec says that what you're doing should work, but it's implemented incorrectly in every major browser besides Internet Explorer / Edge, making multi-line inline-flex column layouts useless at present for most developers. Here's a Chromium bug report providing an example that is effectively identical to yours, and noting that it renders incorrectly in Chrome, Safari, and Firefox.
The argument from spec is more complicated than I'm able to understand, but the key point is that Flexible Box Layout Module Level 1 spec defines the intrinsic cross-size of a flex container (that is, the intrinsic height of a flex-direction: row flex container or the intrinsic width of a flex-direction: column flex container) in the section Flex Container Intrinsic Cross Size. There, it is stated:
For a multi-line flex container, the min-content/max-content cross size is the sum of the flex line cross sizes
That is, the intrinsic width of a flex-direction: column flex container should be the sum of the widths of its columns, as you'd expect. (There is more complexity than this, and I don't understand it all, but I believe the above to be broadly true.) However, Chrome, Firefox, and Safari all calculate this width incorrectly; setting width: min-content or width: max-content on a column wrap flex box in Chrome, you can clearly see that the width is set to the width of the widest single element.
A silly Chrome-specific workaround exists, but is probably best avoided. Until the bug is fixed, this part of the Flexbox model simply doesn't work as designed and there's no clean solution available.
It seems this issue cannot be solved only with CSS, so I propose you a JQuery solution
container width = position of the last child - position of the container + width of the last child (including margin)
Code :
$(document).ready(function() {
$('.container').each(function( index ) {
var lastChild = $(this).children().last();
var newWidth = lastChild.position().left - $(this).position().left + lastChild.outerWidth(true);
$(this).width(newWidth);
})
});
Demo :
http://jsfiddle.net/qzea320L/
You have a column layout distribution with a fixed height container.
When you set the flex-direction to column you define the Vertical axis as the main axis.
In flexbox that means it will fill up the available height and then create a new column.
In this JSBIN I use javascript to change the container's height and, because of that, you will see the child items move.
PS: you shouldn't rely on IE behavior since their flex support is recent.
Another possible approach:
.container {
column-count: 2; /*or whatever */
}
.container > div {
display: inline-block;
}
https://developer.mozilla.org/en-US/docs/Web/CSS/column-count
You may also need to adjust margin-top of .container > div:first-child if they don't align to the top.