When a flexbox item has wrapping text the item fills the container and has extra whitespace at the end - html

Using a flexbox layout, I have a container with three divs in it. What I'm going for is the first div hugging the left edge of the container while the remaining two divs snuggle up on the right edge of the container. I'm admittedly very new to using the flex layout, but it seems straightforward enough.
Simple html:
<div class="container">
<div class="one">Left</div>
<div class="two">Middle</div>
<div class="three">Right</div>
</div>
And associated css:
.container{
display: flex;
align-items: center;
}
.one{
margin-right:auto;
}
The above code usually displays exactly as intended, and for most cases is perfect. Where trouble rears up is when, for example, that third item has enough text to wrap. When this happens the third item fills with whitespace to the right of the text, leaving no space between items one and two. It is subtle with small words, but pretty obvious with two large words that break right in the middle. The following codepen explains it better than I can.
Codepen showing the issue: http://codepen.io/camwheel/pen/XjjyOx
Am I abusing/misusing flex syntax here, or is there a legitimate issue with wrapped text in a flex item? More importantly in the short term, is there anything I can do to get around this issue?

If your divs only contain text then you can do the following:
Skip flexing the last item
text-align: right on the middle element
.container {
display: flex;
background: salmon;
}
.one, .two {
flex: 1;
}
.two {
text-align: right;
}
<div class="container">
<div class="one">Left</div>
<div class="two">Middle</div>
<div class="three">Right</div>
</div>

As Michael_B pointed out in comments, this isn't a flex issue. It comes down to a limitation of CSS. Parent items don't know when child items shrink (which happens to the width when text wraps) and so can't collapse back down. For a more in depth assessment see his earlier post here.

Related

Prevent text from expanding div without JS or table-caption

My issue is solved using display:table-caption but that seems incorrect (since it's not actually a table caption). So my question is what is table-caption doing that solves my issue, and can that be achieved with different CSS?
My original issue: I have a container div and within that are header, details, and footer divs. The header (and less importantly footer) text should remain on a single line (no wrapping) and the container div should shrink-wrap (expand and shrink width) to fit those single line elements. However, the details div should NOT be allowed to expand the container div. Instead the details text should simply wrap as per whatever width the container happens to be (which would be a result of the length of the header and/or footer text).
I have found that this seems to be achievable using the display:table-caption style.. however this doesn't seem like an appropriate style to use (on something that isn't a table caption).
See example: https://jsfiddle.net/uac032q4/1/
Note the display: table-caption in the .container's style.
.container {
display: table-caption;
background-color: white;
}
.heading {
font-size: 4em;
}
.details {
background-color: yellow;
}
.nowrap {
white-space: nowrap;
}
<div class="container">
<div class="heading nowrap">Strech Dont Wrap</div>
<div class="details">
This is the paragraph that should wrap down once it reaches the available width provided by the container box which in turn is stretched to that width by the nowrap heading text.
</div>
<div class="nowrap">Footer</div>
</div>
So, using only CSS (no JS) and without fixed widths: is there an alternate CSS solution without using table-caption? and what is table-caption doing that is solving the wrapping issue?
Note that there is an existing question that is essentially the same thing, however the accepted answer uses JS and the alternate answer uses display:table-caption:
Prevent text from expanding shrink wrapped div

Keeping flexbox container centered when child text wraps [duplicate]

This question already has answers here:
Make container shrink-to-fit child elements as they wrap
(4 answers)
Closed 5 years ago.
I am trying to center two div elements side by side in a row with flexbox (display:flex).
The first div element, on the left, just has an image.
The second div element, on the right, has left-aligned inline text of unspecified length.
When the line of text is short enough to fit on one line, both divs are aligned and justified to the center as I expect.
When the line of text is long enough to wrap onto two lines, the second div element does not wrap itself around the content as I expect. Instead, it leaves a large white space on the right side of the div.
I mocked up an example here: http://codepen.io/anon/pen/NGqYQX?editors=110. Vary your browser window's width to see what I mean.
How can I set the second div element to shrink itself to fit the text, so that both div elements appear centered?
.flexbox-container {
display: flex;
justify-content: center;
align-items: center;
}
.div1 {
margin-right: 30px;
}
.div2 {
font-size: 48px;
line-height: 48px;
text-align: left;
}
<div class="flexbox-container">
<div class="div1">
<img src="http://dreamatico.com/data_images/kitten/kitten-2.jpg" width="150px">
</div>
<div class="div2">
This is an example of a line of text.
</div>
</div>
<br>
<div class="flexbox-container">
<div class="div1">
<img src="http://dreamatico.com/data_images/kitten/kitten-2.jpg" width="150px">
</div>
<div class="div2">
This is an example of a much loooooooooooooonger line of text.
</div>
</div>
Here is a Photoshop mockup showing what I am trying to do:
In order to achieve your goal (as specified in your second image) we need to make a few adjustments to your HTML and CSS. Everything can be done with flexbox.
HTML
<div id="flex-container-main">
<div class="flex-container-child">
<figure>
<img src="http://dreamatico.com/data_images/kitten/kitten-2.jpg" width="150px">
</figure>
<p>This is an example of a line of text.</p>
</div>
<div class="flex-container-child">
<figure>
<img src="http://dreamatico.com/data_images/kitten/kitten-2.jpg" width="150px">
</figure>
<p>This is an example of a much loooooooooooooonger line of text.</p>
</div>
</div><!-- end #flex-container-main -->
CSS
#flex-container-main {
display: flex;
flex-direction: column;
align-items: center;
}
.flex-container-child {
display: flex;
align-items: center;
min-height: 127px;
width: 75%;
margin: 10px;
}
figure {
margin: 0 20px 0 0;
}
img {
width: 150px;
height: 127px;
vertical-align: bottom;
}
p {
font-size: 48px;
margin: 0;
}
Revised Codepen Demo
Here's what's happening...
Your question asks:
Keeping flexbox centered when text wraps to 2 or more lines
I am trying to center two div elements side by side in a row with flexbox (display:flex).
Let's quickly go over your two images.
Image 1
Image 2
In image 1 all flex items are actually centered. The blue highlight from Chrome Dev Tools emphasizes this point. Each item is perfectly centered on the screen.
Yes, it does get a bit clunky as you re-size the screen smaller – mostly because of the large font size – but the flex items remain centered nonetheless.
In image 2, the flex items are not evenly centered. What you've created in your mock-up is more like a column containing both flexboxes, and the column is centered. But individually only the first row is centered on the screen.
A couple of notes about your code:
With justify-content declared on the flex containers, you are centering the flex items. The flex container itself is not centered.
Since both flexboxes are direct children of the <body>, and <body> has no defined width, the flexboxes align themselves in relation to the viewport.
So to achieve the effect you want we can wrap all your existing mark-up in a new flex container (#flex-container-main). This converts the original flex containers into flex items, which can then be evenly centered as a group.
The new flex items (now classed as .flex-container-child) are given a width to create space and a minimum height based on the height of the image. Each flex item is also declared a flex parent (display: flex) which allows us to use flex properties on child elements. In particular, this is useful for vertically centering the text (as shown in your images).
(Note that my use of HTML semantic elements is not necessary for the code to work. If you prefer the original div tags just swap them back. The important adjustment is the new parent container.)
Lastly (and this may not be important to your layout but just in case), browsers normally give images a small gap of whitespace under their bottom border. This is used to accommodate descenders. With vertical-align: bottom, this space is removed. (For more details see my answer about descenders.)

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.

Two column list without two lists/wrappers

I am hoping to find a way to create a simple two-column list with variable height elements without using two wrapper elements for the columns, or any javascript.
https://stackoverflow.com/a/16821155/2026098 mentions that floating odd elements to the left and even elements to the right might do the trick, but it seems to fail when the elements' heights are too variable.
<div class="two-column-list">
<div class="element odd">A few words</div>
<div class="element even">A few sentences</div>
<div class="element odd">Many paragraphs</div>
<div class="element even">One sentence</div>
<div class="element odd">Many paragraphs</div>
</div>
.element.odd { float: left; }
.element.even { float: right; }
See http://jsfiddle.net/PF62T/3/.
The gray boxes are odd and red ones are even. When making one of the odd boxes not high enough, the last of the even (red boxes), even though floated right, seems to want to occupy the space available on the left. Is there any way I can force the boxes to always stick to the left or right edge, no matter their height?
I don't want to use CSS3 columns because I need to support IE8+. I am using angular so I can easily add different classes for odd and even elements, so this isn't a problem, either.
I realise that using two wrappers would be the most solid solution, but I get the feeling the answer is out there somewhere :P My main reason for not wanting wrapper elements is that I need to show all the elements in a single column on mobile, in which case the wrappers would mess up their order.
You can fix this by using clear:left; to the floated left elements and clear:right on the floated right elements.
See this FIDDLE
div:nth-child(odd) {
float: left;
clear:left; /* ADD THIS */
background-color: red;
}
div:nth-child(even) {
float: right;
clear:right; /* ADD THIS */
}

Vertical middle for an element within a variable height container

I have the following situation:
A variable height div (#container) with an image on the inside (image that is placed within another div) that I need to float:right and align vertically in the middle. How to do this?
Thanks.
EDIT:
Maybe I didn't make it clear enough that i do not know beforehand how much content the container has, from a few lines to a wall of text, so any solution relying on its height won't work (and that's my problem :P)
This is a fiddle with an example of possible content to which align the image: http://jsfiddle.net/9DbmN/
You should take a look at Centering in the Unknown by Chris Coyier. Imo it´s a pretty solid solution to the holy grail of vertical centering.
I would not discourage using tables here))) If you use a two-cell table with vertical-align: middle set on its td elements - it will perfectly (and easily!!!) solve your problem.
If you want to have two containers, one of which (the one with the image) will be floated to the right and needed centering - I'd say you'll have to avoid using float property for this. Because a) as far as I understand you don't need you content on the left to be UNDER the image, right? b) floats are block-level elements and you can't change it even if you set display: table-cell, the browser will still render it as display: block - which leads me to the conclusion that you won't manage to center it by css (at least by the means I'm aware of).
If you don't need ie7 support a possible workaround might be this:
html:
<div id="container">
<div class="content">Content goes here, vertically aligned with the image</div>
<div class="i_used_to_be_floated_right">Image goes here</div>
</div>
css:
#container {
display: table;
width: 100%;
}
.content, .i_used_to_be_floated_right {
display: table-cell;
vertical-align: middle;
}
.content {
background: green;
width: 80%;
}
.i_used_to_be_floated_right{
background: red;
width: 20%;
}
The working example live can be seen here: http://jsfiddle.net/skip405/sDXMj/1/
But if you need ie7 - I would vote for the table-solution I stated at the very beginning.