How to vertically center div relative to flexbox grandparent? - html

Given a div with display flexbox .wrapper (run and see snippet below) and a deeply nested div .text-container, how can I center the deeply nested div relative to the .wrapper and not relative to its parent .variable-height?
In the snippet below there are two columns with equal height and I want the text, which is placed in a variable height div inside each column, to be at the same level. I have set the display of the variable height div also to flexbox, so logically the text is centered, relative to it and not to the grandparent .wrapper, which is not what I want.
The only solution I came up with is to set position: relative; on .wrapper and on .text-container:
position: absolute;
top: 50%;
transform: translateY(-50%);
However, I am not sure, if it's a good idea to mix flexbox and absolute/relative positioning.
.wrapper {
background-color: white;
min-height: 200px;
width: 200px;
float: left;
margin-left: 50px;
display: flex;
flex-direction: column;
}
.wrapper .fixed-height {
background-color: orange;
min-height: 30px;
}
.wrapper .second {
background-color: yellow;
min-height: 30px;
}
.wrapper .variable-height {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
.wrapper .variable-height .text-container {
width: 100%;
text-align: center;
}
<div class="wrapper">
<div class="fixed-height"></div>
<div class="fixed-height second"></div>
<div class="variable-height">
<div class="text-container">
<div class="title">Title</div>
<div class="subtitle">Subtitle</div>
</div>
</div>
<div class="fixed-height"></div>
</div>
<div class="wrapper">
<div class="fixed-height"></div>
<div class="variable-height">
<div class="text-container">
<div class="title">Title</div>
<div class="subtitle">Subtitle</div>
</div>
</div>
<div class="fixed-height"></div>
</div>

However, I am not sure, if it's a good idea to mix flexbox and absolute/relative positioning.
Well, it depends on what sort of behavior is acceptable to you.
When you absolutely position an element, you remove it from the document flow.
So, in this case, .wrapper and its flex items don't know that .text-container exists. If there's any flexibility to the container or items, they will overlap with .text-container. See this illustration:
Centering: Absolute Positioning vs Flexbox (re-size the window to see the difference)
Again, if the overlapping is acceptable to you, then absolute positioning is fine.
In terms of the flexbox specification, there's nothing wrong with mixing absolute / relative positioning with flex properties. The spec has a section on this subject:
4.1. Absolutely-Positioned Flex Children
An absolutely-positioned child of a flex container does not participate in flex layout. However, it does participate in the reordering step (see order), which has an effect in their painting order.
The static position of an absolutely-positioned child of a flex container is determined such that the child is positioned as if it were the sole flex item in the flex container, assuming both the child and the flex container were fixed-size boxes of their used size.
The effect of this is that if you set, for example, align-content: center; on an absolutely-positioned child of a flex container, the child’s static position will center it in the flex container’s cross axis.
Two things to note from the spec:
Although an absolutely positioned flex item is removed from the document flow (as expected), it still recognizes the order property.
You can still use flex properties to center an absolutely positioned flex item, but only under certain circumstances and only within the parent container, not the grandparent.

Related

How to make 2 divs the same height

I am trying to get 2 divs that do not have a common parent div to be the height of the larger div. (using display: flex).
As shown in the above code, I would like <child-div1> and <child-div2> to have the same height. Currently, I have display: flex on the <parent-div> which successfully makes <middle-div1> and <middle-div2> to have the same height. However, I can't seem to figure out how to ensure that <child-div1> and <child-div2> have the same height.
<parent-div style="display: flex">
<middle-div1>
<child-div1></child-div1>
</middle-div1>
<middle-div2>
<child-div2></child-div2>
</middle-div2>
</parent-div>
In order to figure it out you could right click in the page, then select inspect element, and you going to see a window like this:
by clicking the most left icon and hovering over the two divs, you are going to see the exact width x height
Add display: flex to the <middle-div> flex items.
This will automatically apply align-items: stretch to the <child-div> children. (Just note that a height rule will override align-items: stretch.)
With that layout, you can set both children to have height: inherit; it's inheriting the height from the tallest part of the container. So for example, if you have an image on one side that is 400 px tall, that stretches the container, therefore, allowing the other child to grow in height also.
.container {
display: flex;
}
.one, .two {
height: inherit;
width: 50%;
}
.one {
background-color: lightblue;
}
.two {
background-color: darkgreen;
color: white;
}
<div class="container">
<div class="one">1<br><img src="https://dummyimage.com/400x400/000/fff&text=test"></div>
<div class="two">2</div>
</div>
One solution is to add height:100%; to child-div1 and child-div2
<parent-div style = "display: flex">
<middle-div1>
<child-div1 style="height: 100%;"></child-div1>
</middle-div1>
<middle-div2>
<child-div2 style="height: 100%;"></child-div2>
</middle-div2>
</parent-div>

Flexbox preventing margins from being respected

I'm struggling to get a flexbox layout working with margins and overflow: hidden.
The carousel component I'm integrating uses this method to be responsive but as soon as a display:flex is applied to the parent, the margin and overflow: hidden properties are ignored as shown in the example below.
The top version is not nested in a flexbox where as the one below it is, and is not respecting the margins of the parent.
The issue is demonstrated in the code below and in this Plunker.
http://plnkr.co/edit/qU1oq1Vq1X3FQMWLJiQk?p=preview
body {
margin: 400px;
}
.overflowHidden {
overflow: hidden;
}
.flex {
display: flex;
}
<div>
<div class="overflowHidden">
<img src="http://lorempixel.com/600/400" />
</div>
</div>
<div class="flex">
<div>
<div class="overflowHidden">
<img src="http://lorempixel.com/600/400" />
</div>
</div>
</div>
I know there are many similar questions on this topic but I couldn't find anything relating to my particular use case.
Here's the solution:
.flex > div { overflow: hidden }
revised demo
The first thing to note is that margins have nothing to do with this problem. Margins are, in fact, being respected. They are simply travelling with the element, which is expanding a container.
The non-flex div containers have display: block by default.
In a block formatting context, when an image becomes too big to fit in a container with overflow: hidden, it simply disappears, as expected.
The parent divs and their margins remain stable and undisturbed.
However, in a flex formatting context the behavior is different.
First, you have three levels of divs in the flex example. You only have two in the block example. But that's not really important.
The key piece of information here is this:
When you apply display: flex or display: inline-flex to an element it becomes a flex container and its children become flex items. By default, flex items cannot be smaller than the size of their content. They have a default setting of min-width: auto.
So, when an image becomes too big for its parent, the parent (if it's a flex item) must expand. But in your example, the parent of the image (div.overflowHidden) is not a flex item, it's a standard block element. So overflow: hidden is working fine (just like in the block example).
But the parent of the parent is a flex item. And its default setting is min-width: auto. And it cannot shrink below the size of its content (the image). So while the block example can remain on the screen at all times, the flex version will overflow the body / viewport, taking the right margin with it.
The solution is to override the min-width: auto default on the flex item. You can use:
min-width: 0
OR
overflow: hidden
More details here: Why doesn't flex item shrink past content size?
Not sure why that's happening exactly, but seems to work if you apply .overflowHidden to either an element that wraps the flex parent, or just apply it to the flex parent instead.
body {
margin: 400px;
overflow-x: hidden;
}
.overflowHidden {
overflow: hidden;
}
.flex {
display: flex;
z-index: -1;
position: relative;
}
<div>
<div class="overflowHidden">
<img src="http://lorempixel.com/600/400" />
</div>
</div>
<div class="flex overflowHidden">
<div>
<img src="http://lorempixel.com/600/400" />
</div>
</div>
<div class="overflowHidden">
<div class="flex">
<img src="http://lorempixel.com/600/400" />
</div>
</div>

Why does height 100% work on absolutely positioned element?

As far as I know for the height to work as percentage the container element must have a specific height mentioned. But this doesn't hold true for absolutely positioned element with the ancestor being relatively positioned. Here is a working example of what I meant:
.container {
width: 400px;
background: cyan;
text-align: right;
position: relative;
color: white;
}
.child {
width: 90%;
height: 100%;
background: blue;
}
.absolute {
position: absolute;
}
.second {
margin-top: 30px;
}
<div class="container">
<div class="child absolute">Absolute</div>
one <br> two <br> three <br> one <br> two <br> three <br>
</div>
<div class="container second">
<div class="child">Static</div>
one <br> two <br> three <br> one <br> two <br> three <br>
</div>
As you can see the absolutely placed div applied 100% height onto it but not the statically positioned div. Why?
From MDN
relative
This keyword lays out all elements as though the element were not positioned, and then adjusts the element's position, without changing layout (and thus leaving a gap for the element where it would have been had it not been positioned). The effect of position:relative on table-*-group, table-row, table-column, table-cell, and table-caption elements is undefined.
Read more. Is very nicely described.
Here is a great read about the different position types:
Absolute is relative to the parent element and is not affected by other elements and are removed from the flow of the page i.e. you can see the list with one, two, three unaffected.
It's height is 100% as .child specifies.

Elements in a flex container are not wrapping

When I try to insert block elements in a flex container, they all stay on the same line as if they were inline-blocks.
I would like the two first div's to be on the same line, and the last one to be on a second line. Sadly, that doesn't seem to work.
Anyone have any idea ?
<div style="display: flex">
<div style="display: inline-block">
This is an inline block element
</div>
<div style="display: inline-block">
This is an inline block element
</div>
<div style="display: block">
This is a block element
</div>
</div>
An initial setting of a flex container is flex-wrap: nowrap. This means flex items are forced to remain in a single line.
You can override the default with flex-wrap: wrap.
The display value of flex items is ignored in flex layout.
A flex container, which is an element with display: flex or display: inline-flex, establishes a flex formatting context. Although similar to a block formatting context, there are differences.
One difference is that children of a flex container ignore the display property.
Another difference is that, in a flex container, margins don't collapse, and the float and clear properties have no effect.
A flex container also comes with several default settings. Among them:
justify-content: flex-start - flex items will stack at the start of the line
flex-shrink: 1 - flex items are allowed to shrink and will not overflow the container
align-items: stretch - flex items will expand to cover the cross-size of the container
flex-direction: row - flex items will align horizontally
flex-wrap: nowrap - flex items are forced to stay in a single line
Note the last two items.
Flex items will line up in a row and cannot wrap.
If you want to have two flex items on the first line, and a third item on the second line, allow the container to be multi-line with flex-wrap: wrap.
.container {
display: flex;
flex-wrap: wrap;
}
.box {
flex: 0 0 45%;
height: 50px;
margin: 5px;
background-color: lightgreen;
border: 1px solid #ccc;
}
<div class="container">
<div class="box"></div>
<div class="box"></div>
<div class="box"></div>
</div>
Also, if you want flex containers to display inline, use display: inline-flex not display: flex. These are comparable to display: inline-block and display: block.
use flex-wrap:wrap in parent because by default the flex-wrap is nowrap
use flex-basis:50% in child, to divide both inline-block elements in same size.
See more detailed info about flexbox on this article: A Complete Guide to Flexbox
*,
*::before,
*::after {
box-sizing: border-box
}
body {
margin: 0
}
.flex {
display: flex;
flex-wrap: wrap
}
.flex div {
flex: 0 50%; /*change to 1 50% to see the difference */
border: 1px solid black;
padding: 10px
}
<div class="flex">
<div>
This is an inline block element
</div>
<div>
This is an inline block element
</div>
<div>
This is a block element
</div>
</div>

Why does display: flex push the following element onto a new line?

In my code, the first example works fine. The second one pushes the second element to a new line.
Why? and how to avoid it?
see this codepen
.one {
background-color: cornflowerblue;
width: 50%;
}
.one,
.two {
height: 100%;
font-size: 30px;
}
.two {
width: 50%;
background-color: salmon;
}
.ib {
display: inline-block;
}
.container {
font-size: 0;
height: 200px;
}
.fb {
display: flex;
}
<div class="container">
<div class="one ib">one</div>
<div class="two ib">two</div>
</div>
<div class="container">
<div class="one fb">one</div>
<div class="two ib">two</div>
</div>
You have three divs that have their display value set to inline-block. Each of these divs has a class ib.
You have one div with display: flex. This div has a class fb.
When you apply display: flex to an element it becomes a block-level container. As a block-level element, it takes up all available space in the line, pushing any subsequent elements to the next line.
The way to solve this problem is to use display: inline-flex instead of display: flex.
Revised Codepen
From the spec:
3. Flex Containers: the flex and inline-flex display
values
flex
This value causes an element to generate a block-level flex container
box.
inline-flex
This value causes an element to generate an inline-level flex
container box.
.fb{
display: inline-flex;
}
If you use the above code you could see the difference easily.
An inline element does not start on a new line and only takes up as much width as necessary.
But flex will start on a new line .
flex is actually the property you apply and enable at a container/parent level. It's not clear from your example what exactly you expect the your child divs (one and two) to do?
See this updated pen where I've removed the ib and fb classes from the children and applied fb to the second container parent: http://codepen.io/angeliquejw/pen/qZRoLz?editors=1100
Also, CSS-Tricks has a fab breakdown that helps understand the basics of flexbox: https://css-tricks.com/snippets/css/a-guide-to-flexbox/
Updated to reflect response below: flex, which you used initially,behaves more like block (see docs: https://developer.mozilla.org/en-US/docs/Web/CSS/display). You want to use display:inline-flex : http://codepen.io/angeliquejw/pen/jqyxNz?editors=1100