Flexbox child collapsed after aligning - css

Can't figure out how to keep the child items same height on both sides after aligning their content to the middle.
My aim is to create a layout as seen in picture:
<div class="halves">
<div class="half">
<div class="half-inner is-right">H1</div>
</div>
<div class="half">
<div class="half-inner is-left">H2
<br> asdfs df <br>a sdfadsf sdfa dadsf df asdf afdf sadf asdf </div>
</div>
.halves{
display: flex;
border: 5px solid red;
.half{
flex: 1;
border: 10px solid yellow;
/* align-items: center; */
display: flex;
.half-inner{
max-width: 100px;
&.is-right{
margin-left: auto;
background: pink;
}
&.is-left{
background: green;
}
}
}
}
My current code here: http://codepen.io/zsitro/pen/YqpLba
In my example uncommenting /* align-items: center; */ you can see the child item collapses.
I appreciate any guidance. ty

align-items: center forces the div into the center of a flex container, and in the event of no specific declarations of width/height/flex-basis/whatnot, it makes the div as big as its content, preserving the whitespace. If you want the div to stretch, you will need the the align-items value to be stretch so it takes up the entire height of the parent container.
Additionally, if you want the .half-inner divs to be equal in width: flex: 1 (or flex-grow: 1) is a child property, so the declaration you currently have only applies to the .half divs, not the .half-inner divs. Since the parent .half div is a flex container, you can just add flex:1 under the .half-inner CSS and you should be good to go.
.halves{
display: flex;
border: 5px solid red;
.half{
flex: 1;
border: 10px solid yellow;
align-items: stretch;
display: flex;
.half-inner{
max-width: 100px;
flex: 1;
&.is-right{
margin-left: auto;
background: pink;
}
&.is-left{
background: green;
}
}
}
}

Related

My sticky side bar is not sticky which is a child of flex, why?

I have a parent container which is flex and has 2 children. One of the is content and the other is sidebar which I want to be sticky. But it is not working. It's not sticky at all. Could you please tell me what I am doing wrong??
HTML
<div class="container">
<div class="side-bar">Side bar</div>
<div class="content">Content</div>
</div>
CSS
.container {
display: flex;
}
.side-bar {
box-shadow: 0 0 1px grey;
padding: 1rem 0.4rem;
align-self: start;
display: none;
flex-basis: 14rem;
margin-right: 0.7rem;
position:sticky;
top:1rem;
}
if you are using flex in the parent element use align-self: flex-start for the element which you want to make sticky.
Apart from that, your element is hidden with display:none.
.side-bar {
box-shadow: 0 0 1px grey;
padding: 1rem 0.4rem;
align-self: flex-start;
display: none; // --->> remove this line to show the element
flex-basis: 14rem;
margin-right: 0.7rem;
position:sticky;
top:1rem;
}

Image in flexbox column not rendering properly in Chrome

I have a div with one image and one paragraph in it.
<div id="container">
<img src="..." />
<p>
This is my text
</p>
</div>
I use flex-box and flex-direction: column to align them.
#container {
display: flex;
flex-direction: column;
height: 300px;
width: 400px;
}
img {
max-width: 80%;
flex-basis: 50%;
}
p {
flex-basis: 50%;
}
Since both img and p have flex-basis 50% I would expect each of them to take up 50% of the space. In Firefox it works, but in Chrome the image is bigger (in height) than the container itself.
I have made a jsfiddle to demonstrate this: https://jsfiddle.net/q2esvro9/1/
How can I get the behaviour from Firefox in Chrome?
(Another interesting fact: In Internet Explorer 11 the image and text take up the same space, but the image is stretched in width. Which means 3 different behaviours for a very short and simple CSS code)
#container {
display: flex;
align-items: center;
align-content: center;
text-align: center;
flex-direction: column;
border: solid 2px red;
height: 300px;
width: 400px;
}
img {
max-width: 80%;
flex-basis: 50%;
}
p {
flex-basis: 50%;
border: solid 2px green;
}
<div id="container">
<img src="https://image.freepik.com/free-icon/apple-logo_318-40184.jpg" />
<p>
This is my text
</p>
</div>
There are flexbox rendering variations between the major browsers.
When dealing with images, the number of variations grows.
What I've found to work consistently across browsers is to not use img elements in a flex formatting context (i.e., don't make them flex items).
Instead, wrap an img in a div element, making the div the flex item and keeping the image in a block formatting context.
#container {
display: flex;
align-items: center;
align-content: center;
text-align: center;
flex-direction: column;
border: solid 2px red;
height: 300px;
width: 400px;
}
#container > div {
flex: 0 0 50%; /* 1 */
min-height: 0; /* 2 */
}
img {
height: 100%;
}
p {
flex-basis: 50%;
border: solid 2px green;
}
<div id="container">
<div>
<img src="https://image.freepik.com/free-icon/apple-logo_318-40184.jpg" />
</div>
<p>
This is my text
</p>
</div>
Notes:
The meaning and benefits of flex: 1
Why don't flex items shrink past content size?

How to enable a scrollbar on a flex item?

I have a flexbox-based layout with two panels (top and bottom) occupying 1/3 and 2/3 of the viewport, respectively. (Actually there are more panels, but I've distilled it to the minimal example).
The top panel is also a flex container, because I want its children to flow top to bottom and be vertically centered when there is room. When there is more stuff in top panel than would fit in it, I want it to be scrollable, hence overflow: auto.
The problem: the contents of top shrink to its size, even with flex-shrink: 0, and the scrollbar never pops up.
Observe how the content is shrunk in the following demo, even though it has an explicitly specified height:
html, body {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
.main {
display: flex;
height: 100%;
flex-direction: column;
}
.top {
display: flex;
flex-direction: column;
flex-basis: 33%;
border-bottom: 1px solid #ccc;
overflow: auto;
justify-content: center;
padding: 20px;
}
.bottom {
overflow: auto;
flex-basis: 67%;
}
.content {
height: 500px;
background-color: #eee;
}
<div class="main">
<div class="top">
<div class="content">Content</div>
</div>
<div class="bottom"></div>
</div>
The questions:
How to fix this while preserving the layout requirements? Disabling display: flex for the top panel gives the desired effect in the demo. I could position contents of top in a flexboxless way, but I lose the benefits of flex layout and the automatic vertical centering.
Why does this happen? References to CSS spec would be welcome.
You wrote:
The problem: the contents of top shrink to its size, even with flex-shrink: 0, and the scrollbar never pops up.
Actually, the solution is flex-shrink: 0. So the question becomes, where did you apply it?
It wouldn't work if you applied it to top – a flex item in the primary container with flex-basis: 33% (i.e., height: 33%, in this case) – because the length of top is a percentage. As such, it will shrink / expand naturally as percentage lengths are relative to the parent container.
You need to apply flex-shrink: 0 to .content – a flex item in the nested container with a fixed height (height: 500px / flex-basis: 500px).
So this will work:
.content {
height: 500px;
flex-shrink: 0;
}
or this:
.content {
flex-basis: 500px;
flex-shrink: 0;
}
or, better yet, this:
.content {
flex: 0 0 500px; /* don't grow, don't shrink, stay fixed at 500px */
}
From the spec:
7.2. Components of
Flexibility
Authors are encouraged to control flexibility using the flex shorthand
rather than with its longhand properties directly, as the shorthand
correctly resets any unspecified components to accommodate common
uses.
body {
margin: 0;
}
.main {
display: flex;
flex-direction: column;
height: 100vh;
}
.top {
display: flex;
flex-direction: column;
flex-basis: 33%;
border-bottom: 1px solid #ccc;
overflow: auto;
justify-content: center;
padding: 20px;
}
.bottom {
overflow: auto;
flex-basis: 67%;
}
.content {
flex: 0 0 500px;
background-color: #eee;
}
<div class="main">
<div class="top">
<div class="content">Content</div>
</div>
<div class="bottom"></div>
</div>
Then you have a second problem, which is that the upper section of the top element gets cut off and is inaccessible via scroll. This is caused by justify-content: center on the container.
This is a known issue. It is solved by using flex auto margins.
So instead of this:
.top {
display: flex;
flex-direction: column;
flex-basis: 33%;
border-bottom: 1px solid #ccc;
overflow: auto;
/* justify-content: center; <--- REMOVE */
padding: 20px;
}
do this:
.content {
flex: 0 0 500px;
margin: auto 0; /* top & bottom auto margins */
background-color: #eee;
}
body {
margin: 0;
}
.main {
display: flex;
flex-direction: column;
height: 100vh;
}
.top {
display: flex;
flex-direction: column;
flex-basis: 33%;
border-bottom: 1px solid #ccc;
overflow: auto;
/* justify-content: center; USE AUTO MARGINS ON FLEX ITEM INSTEAD */
padding: 20px;
}
.bottom {
overflow: auto;
flex-basis: 67%;
}
.content {
flex: 0 0 500px;
margin: auto 0;
background-color: #eee;
}
<div class="main">
<div class="top">
<div class="content">Content</div>
</div>
<div class="bottom"></div>
</div>
Here's a complete explanation:
Can't scroll to top of flex item that is overflowing container
The scrollbar appears when there are enough .content element shrinked to their very minimal height (one line height in this case).
That's not really how things work with flex. height is not strictly respected. If you still want to work with height, you can fix this by setting a min-height to .content according to the minimum height you want for them.
Or you can instead set flex on .content (and get rid of height):
css
flex: 100px 1 0;
Which will set a minimum height (flex-basis) of 100px, flex-grow at 1 so that it takes all the available space, and flex-shrink at 0 so that the element is always at least 100px tall.

Flex auto margins behave differently in Chrome and Safari

Open this page in Chrome and in Safari and we can see the difference.
In Safari the "Maths" keyword is not on the right side as in Chrome.
Also the widths of the item-3 div is different in Chrome and in Safari.
My issue is that why it is different in Safari and what is the fix (I want CSS fix on the item-3).
flex-wrap: wrap is not a acceptable solution.
.main-container {
display: flex;
flex-direction: column;
width: 400px;
height: 300px;
border: 1px solid red;
}
.item-1 {
display: flex;
border: 1px solid blue;
}
.item-2 {
display: flex;
border: 1px solid green;
}
.item-3 {
display: flex;
margin-left: auto!important;
border: 2px solid yellow;
}
<div class="main-container">
<div class="item-1">Physics</div>
<div class="item-2">Chemistry</div>
<div class="item-3">Maths</div>
</div>
The width of .item-3 is different than its siblings because margin-left: auto packs the flex item to the right side. That's normal behavior for flex auto margins.
The reason margin-left doesn't work in Safari is a mystery / bug.
However, for cross-browser support, there is a simple flex alternative to auto margins in this case: align-self: flex-end
.main-container {
display: flex;
flex-direction: column;
width: 400px;
height: 300px;
border: 1px solid red;
}
.item-1 {
display: flex;
border: 1px solid blue;
}
.item-2 {
display: flex;
border: 1px solid green;
}
.item-3 {
display: flex;
align-self: flex-end; /* NEW */
border: 2px solid yellow;
}
<div class="main-container">
<div class="item-1">Physics</div>
<div class="item-2">Chemistry</div>
<div class="item-3"><span>Maths</span></div>
</div>
If you want .item-3 to keep the full width of the container, then use justify-content: flex-end.
.main-container {
display: flex;
flex-direction: column;
width: 400px;
height: 300px;
border: 1px solid red;
}
.item-1 {
display: flex;
border: 1px solid blue;
}
.item-2 {
display: flex;
border: 1px solid green;
}
.item-3 {
display: flex;
justify-content: flex-end; /* NEW */
border: 2px solid yellow;
}
<div class="main-container">
<div class="item-1">Physics</div>
<div class="item-2">Chemistry</div>
<div class="item-3">Maths</div>
</div>
How align-self: flex-end works
With align-self: flex-end you're shifting .item-3 along the cross axis all the way to the right.
This works because the flex container (.main-container) has flex-direction: column, which makes the main axis vertical and cross axis horizontal.
How justify-content: flex-end works
With justify-content: flex-end you're shifting the children of .item-3 along the main axis all the way to the right.
This works because .item-3 is a flex container with flex-direction: row (by default), which makes the main axis horizontal and cross axis vertical.
Then, as per the specification, text in a flex container that is not explicitly wrapped by an element, is considered an anonymous flex item. This allows justify-content to work.
4. Flex Items
Each in-flow child of a flex container becomes a flex item, and each
contiguous run of text that is directly contained inside a flex
container is wrapped in an anonymous flex item.
Why text-align: right doesn't work
text-align: right won't work because flex items (including anonymous ones) are considered block-level elements. The text-align property applies only to inline-level content.
More information
Learn more about flex alignment along the main axis here:
In CSS Flexbox, why are there no "justify-items" and "justify-self" properties?
Learn more about flex alignment along the cross axis here:
How does flex-wrap work with align-self, align-items and align-content?

Css flex overflow issue

I have:
.container {
display:flex;
align-items:center
}
.content {
flex-grow:1
}
in order to align the .content div vertically with css only. The content changes dynamically and that's why I can't use position:absolute; margin-top:50%... styling. Because I never know the exact height of div on each content update.
But in a scenario where .container width changes but height remains, .content overflows .container top because it wraps the text within.
What I'm trying to do is never let .content exceed the top position less than 0. Even the most ideal situation will be preserving the padding-top value of .container and margin-top value of .content. Overflowing bottom will be OK, in fact it'll be my preference.
Any workarounds?
You can make .container height flexible: use min-height instead of height.
.container {
display: flex;
align-items:center;
min-height: 75px;
border: 3px solid blue;
}
.content {
height: 150px;
flex-grow: 1;
border: 3px solid red;
resize: vertical;
overflow: auto;
}
<div class="container">
<div class="content"></div>
</div>
Another possibility is using overflow: auto. However, instead of centering using align-items:center, use margin: auto 0.
.container {
display: flex;
height: 75px;
border: 3px solid blue;
overflow: auto;
resize: vertical;
}
.content {
height: 150px;
flex-grow: 1;
margin: auto 0;
border: 3px solid red;
}
<div class="container">
<div class="content"></div>
</div>

Resources