I have a layout that gets customized by many different clients so the html structure is locked down to
<div class="container">
<div>content</div>
<div>content</div>
<div>content</div>
<div>content</div>
</div>
NOTE: not using bootstrap, container is just the applicable name
In the UI I need to be able to use CSS to make many different layouts from this structure without altering it. There is one in particular I can't figure out. The desired layout is this:
I know that if it were all vertical it would be easily doo-able with simple width and float styles. I also know that throwing a containing div around the first two children would be an easy solution but again requirements are to leave the html unchanged.
I've tried:
.container {
display: flex;
flex-wrap: wrap;
}
.container > div {
width: 33.33335;
height: 400px;
}
.container > div:nth-child(1),
.container > div:nth-child(2) {
height: 200px;
}
setting appropriate heights to the child divs where the first two are half the height of the others.
Similarly, I've tried:
.container > div {
float: left;
}
.container > div {
height: 400px;
width: 33.33333%;
}
.container > div:nth-child(1),
.container > div:nth-child(2) {
height: 200px;
}
and again giving the first two children a height that is half the others. Nothing has worked, in all outcomes either the first two stack and the others do not float/flex up or the the first two do not stack at all.
Can anyone figure a CSS method of styling this structure for the desired UI?
Appreciate the help.
If you can set a fixed height on the container, you can use flexbox with flex-flow: column wrap. The fixed height is necessary to tell flex items where to wrap. Here's an example:
.container {
display: flex;
flex-direction: column;
flex-wrap: wrap;
justify-content: space-around; /* vertical alignment */
align-content: center; /* horizontal alignment */
height: 320px; /* essential for this method to work */
background-color: lightyellow;
border: 1px dashed red;
}
.container>div {
flex: 0 0 90%; /* flex-grow, flex-shrink, flex-basis */
width: 30%;
margin: 5px;
background-color: lightgreen;
}
.container>div:nth-child(1),
.container>div:nth-child(2) {
flex-basis: 40%; /* override flex-basis from rule above */
}
<div class="container">
<div>content</div>
<div>content</div>
<div>content</div>
<div>content</div>
</div>
Related
I'm having a problem with CSS flexbox. I had a working code yesterday yet today when I tested my solution it stopped working for some reason. It has to do with flexbox.
This is the result I want to have:
To be able to position the content with justify-content. This fails
Content should take all the available space so it has flex-grow: 1. This fails, as well.
The footer should be at the bottom since the content would push it down by taking all the available space thanks to flex-grow: 1. This fails.
It seems that whole flexbox stopped working correctly for me.
I believe the problem is that for some reason flexbox does not even respond correctly to this:
`justify-content: flex-start`
If I try any other values there like center, flex-end, etc nothing happens.
Funny thing is that yesterday flexbox was behaving correctly, I could position it around with justify-content and today I can't.
What am I missing here why is not at least justify-content: flex-end or justify-content: center doing behaving correctly and positioning the content?
If I fix the problem that causes justify-content to stop working I believe flex-grow will also work.
Does anyone have an idea why it's misbehaving?
I can get flex to behaving using this playground so I know my code should be working, My code above is exactly what I did here in the playground:
https://demos.scotch.io/visual-guide-to-css3-flexbox-flexbox-playground/demos/
.ikigai {
display: flex;
flex-direction: column;
justify-content: flex-start;
}
.header, .footer {
height: 80px;
margin: 10px;
border: 1px dashed lightgray;
}
.content {
margin: 10px;
border: 1px dashed lightgray;
flex-grow: 1;
}
<div class="ikigai">
<div class="header">this is a header</div>
<div class="content">content</div>
<div class="footer">footer 12</div>
</div>
https://jsfiddle.net/re01pn2x/
Your flex container has no height defined.
Therefore, it defaults to height: auto (content-driven height).
Add this to your code:
.ikigai {
height: 100vh;
}
.ikigai {
display: flex;
flex-direction: column;
/* justify-content: flex-start; */ /* default value; not necessary */
height: 100vh;
}
.header, .footer {
height: 80px;
flex-shrink: 0; /* optional; if you don't want items to shrink vertically */
margin: 10px;
border: 1px dashed lightgray;
}
.content {
margin: 10px;
border: 1px dashed lightgray;
flex-grow: 1;
}
body {
margin: 0; /* override default margins; prevents vertical scrollbar */
}
<div class="ikigai">
<div class="header">this is a header</div>
<div class="content">content</div>
<div class="footer">footer 12</div>
</div>
More details: How to make div 100% height of the browser window?
justify-content
Note that justify-content wasn't working in your code because there was no free space available. This property works by distributing free space in the container. In this case, because the container was defaulting to height: auto, there was only enough space to accommodate the content.
justify-content & flex-grow
Also note that even with a height defined that creates extra space, justify-content will not work if you use flex-grow. Why? Because flex-grow will consume that free space, again leaving no space for justify-content to distribute.
You can fixed using height:100vh;
.ikigai {
display: flex;
flex-direction: column;
justify-content: flex-start;
height: 100vh;
}
.header, .footer {
height: 80px;
margin: 10px;
border: 1px dashed lightgray;
}
.content {
margin: 10px;
border: 1px dashed lightgray;
flex-grow: 1;
}
I want to have a vertical menu with a specific height.
Each child must fill the height of the parent and have middle-aligned text.
The number of children is random, so I have to work with dynamic values.
Div .container contains a random number of children (.item) that always have to fill the height of the parent. To achieve that I used flexbox.
For making links with text aligned to the middle I am using display: table-cell technique. But using table displays requires using a height 100%.
My problem is that .item-inner { height: 100% } is not working in webkit (Chrome).
Is there a fix for this problem?
Or is there a different technique to make all .item fill the height of the parent with text vertical aligned to middle?
Example here jsFiddle, should be viewed in Firefox and Chrome
.container {
height: 20em;
display: flex;
flex-direction: column;
border: 5px solid black
}
.item {
flex: 1;
border-bottom: 1px solid white;
}
.item-inner {
height: 100%;
width: 100%;
display: table;
}
a {
background: orange;
display: table-cell;
vertical-align: middle;
}
<div class="container">
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
</div>
Solution
Use nested flex containers.
Get rid of percentage heights. Get rid of table properties. Get rid of vertical-align. Avoid absolute positioning. Just stick with flexbox all the way through.
Apply display: flex to the flex item (.item), making it a flex container. This automatically sets align-items: stretch, which tells the child (.item-inner) to expand the full height of the parent.
Important: Remove specified heights from flex items for this method to work. If a child has a height specified (e.g. height: 100%), then it will ignore the align-items: stretch coming from the parent. For the stretch default to work, the child's height must compute to auto (full explanation).
Try this (no changes to HTML):
.container {
display: flex;
flex-direction: column;
height: 20em;
border: 5px solid black
}
.item {
display: flex; /* new; nested flex container */
flex: 1;
border-bottom: 1px solid white;
}
.item-inner {
display: flex; /* new; nested flex container */
flex: 1; /* new */
/* height: 100%; <-- remove; unnecessary */
/* width: 100%; <-- remove; unnecessary */
/* display: table; <-- remove; unnecessary */
}
a {
display: flex; /* new; nested flex container */
flex: 1; /* new */
align-items: center; /* new; vertically center text */
background: orange;
/* display: table-cell; <-- remove; unnecessary */
/* vertical-align: middle; <-- remove; unnecessary */
}
<div class="container">
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
</div>
jsFiddle demo
Explanation
My problem is that .item-inner { height: 100% } is not working in
webkit (Chrome).
It's not working because you're using percentage height in a way that doesn't conform with the traditional implementation of the spec.
10.5 Content height: the height property
percentage Specifies a percentage height. The percentage is calculated with respect to the height of the generated box's
containing block. If the height of the containing block is not
specified explicitly and this element is not absolutely positioned, the value computes to auto.
auto The height depends on the values of other properties.
In other words, for percentage height to work on an in-flow child, the parent must have a set height.
In your code, the top-level container has a defined height: .container { height: 20em; }
The third-level container has a defined height: .item-inner { height: 100%; }
But between them, the second-level container – .item – does not have a defined height. Webkit sees that as a missing link.
.item-inner is telling Chrome: give me height: 100%. Chrome looks to the parent (.item) for reference and responds: 100% of what? I don't see anything (ignoring the flex: 1 rule that is there). As a result, it applies height: auto (content height), in accordance with the spec.
Firefox, on the other hand, now accepts a parent's flex height as a reference for the child's percentage height. IE11 and Edge accept flex heights, as well.
Also, Chrome will accept flex-grow as an adequate parent reference if used in conjunction with flex-basis (any numerical value works (auto won't), including flex-basis: 0). As of this writing, however, this solution fails in Safari.
#outer {
display: flex;
flex-direction: column;
height: 300px;
background-color: white;
border: 1px solid red;
}
#middle {
flex-grow: 1;
flex-basis: 1px;
background-color: yellow;
}
#inner {
height: 100%;
background-color: lightgreen;
}
<div id="outer">
<div id="middle">
<div id="inner">
INNER
</div>
</div>
</div>
Four Solutions
1. Specify a height on all parent elements
A reliable cross-browser solution is to specify a height on all parent elements. This prevents missing links, which Webkit-based browsers consider a violation of the spec.
Note that min-height and max-height are not acceptable. It must be the height property.
More details here: Working with the CSS height property and percentage values
2. CSS Relative & Absolute Positioning
Apply position: relative to the parent and position: absolute to the child.
Size the child with height: 100% and width: 100%, or use the offset properties: top: 0, right: 0, bottom: 0, left: 0.
With absolute positioning, percentage height works without a specified height on the parent.
3. Remove unnecessary HTML containers (recommended)
Is there a need for two containers around button? Why not remove .item or .item-inner, or both? Although button elements sometimes fail as flex containers, they can be flex items. Consider making button a child of .container or .item, and removing gratuitous mark-up.
Here's an example:
.container {
height: 20em;
display: flex;
flex-direction: column;
border: 5px solid black
}
a {
flex: 1;
background: orange;
border-bottom: 1px solid white;
display: flex; /* nested flex container (for aligning text) */
align-items: center; /* center text vertically */
justify-content: center; /* center text horizontally */
}
<div class="container">
<a>Button</a>
<a>Button</a>
<a>Button</a>
</div>
4. Nested Flex Containers (recommended)
Get rid of percentage heights. Get rid of table properties. Get rid of vertical-align. Avoid absolute positioning. Just stick with flexbox all the way through.
Apply display: flex to the flex item (.item), making it a flex container. This automatically sets align-items: stretch, which tells the child (.item-inner) to expand the full height of the parent.
Important: Remove specified heights from flex items for this method to work. If a child has a height specified (e.g. height: 100%), then it will ignore the align-items: stretch coming from the parent. For the stretch default to work, the child's height must compute to auto (full explanation).
Try this (no changes to HTML):
.container {
display: flex;
flex-direction: column;
height: 20em;
border: 5px solid black
}
.item {
display: flex; /* new; nested flex container */
flex: 1;
border-bottom: 1px solid white;
}
.item-inner {
display: flex; /* new; nested flex container */
flex: 1; /* new */
/* height: 100%; <-- remove; unnecessary */
/* width: 100%; <-- remove; unnecessary */
/* display: table; <-- remove; unnecessary */
}
a {
display: flex; /* new; nested flex container */
flex: 1; /* new */
align-items: center; /* new; vertically center text */
background: orange;
/* display: table-cell; <-- remove; unnecessary */
/* vertical-align: middle; <-- remove; unnecessary */
}
<div class="container">
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
</div>
jsFiddle
Specifying a flex attribute to the container worked for me:
.container {
flex: 0 0 auto;
}
This ensures the height is set and doesn't grow either.
Solution: Remove height: 100% in .item-inner and add display: flex in .item
Demo: https://codepen.io/tronghiep92/pen/NvzVoo
For Mobile Safari There is a Browser fix. you need to add -webkit-box for iOS devices.
Ex.
display: flex;
display: -webkit-box;
flex-direction: column;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-webkit-flex-direction: column;
align-items: stretch;
if you're using align-items: stretch; property for parent element, remove the height : 100% from the child element.
I have had a similar issue in iOS 8, 9 and 10 and the info above couldn't fix it, however I did discover a solution after a day of working on this. Granted it won't work for everyone but in my case my items were stacked in a column and had 0 height when it should have been content height. Switching the css to be row and wrap fixed the issue. This only works if you have a single item and they are stacked but since it took me a day to find this out I thought I should share my fix!
.wrapper {
flex-direction: column; // <-- Remove this line
flex-direction: row; // <-- replace it with
flex-wrap: wrap; // <-- Add wrapping
}
.item {
width: 100%;
}
I've recently starting using flexbox and this is the first problem I've run into. I want my .wrp class below to remain display: inline-block; but one line seems to disable this value. That line is: flex-direction: column. When I remove that line my .wrp class starts behaving like an inline-block element again but then of course it loses it's flex-direction value. Is there a simple solution that doesn't require restructuring my HTML too much to keep the flex-direction behavior of flexbox but also keep the inline-block behavior on .wrp?
.contr {
display: flex;
flex-direction: column; /* this line seems to be breakig my display on .wrp */
justify-content: center;
height: 10rem;
background-color: #eee;
}
.wrp {
display: inline-block;
height: 5rem;
background-color: #ddd;
}
p {
width: 100%;
background-color: #ccc;
}
<div class="contr">
<div class="wrp">
<p>I want this paragraph to stretch to fit it's content. Not full width.</p>
</div>
</div>
You can't have an inline-block element within a flex. It looks like you may be looking for display: inline-table:
.contr {
display: flex;
flex-direction: column; /* this line seems to be breakig my display on .wrp */
justify-content: center;
height: 10rem;
background-color: #eee;
}
.wrp {
display: inline-table;
height: 5rem;
background-color: #ddd;
}
p {
width: 100%;
background-color: #ccc;
}
<div class="contr">
<div class="wrp">
<p>I want this paragraph to stretch to fit it's content. Not full width.</p>
</div>
</div>
Hope this helps! :)
I want to have a vertical menu with a specific height.
Each child must fill the height of the parent and have middle-aligned text.
The number of children is random, so I have to work with dynamic values.
Div .container contains a random number of children (.item) that always have to fill the height of the parent. To achieve that I used flexbox.
For making links with text aligned to the middle I am using display: table-cell technique. But using table displays requires using a height 100%.
My problem is that .item-inner { height: 100% } is not working in webkit (Chrome).
Is there a fix for this problem?
Or is there a different technique to make all .item fill the height of the parent with text vertical aligned to middle?
Example here jsFiddle, should be viewed in Firefox and Chrome
.container {
height: 20em;
display: flex;
flex-direction: column;
border: 5px solid black
}
.item {
flex: 1;
border-bottom: 1px solid white;
}
.item-inner {
height: 100%;
width: 100%;
display: table;
}
a {
background: orange;
display: table-cell;
vertical-align: middle;
}
<div class="container">
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
</div>
Solution
Use nested flex containers.
Get rid of percentage heights. Get rid of table properties. Get rid of vertical-align. Avoid absolute positioning. Just stick with flexbox all the way through.
Apply display: flex to the flex item (.item), making it a flex container. This automatically sets align-items: stretch, which tells the child (.item-inner) to expand the full height of the parent.
Important: Remove specified heights from flex items for this method to work. If a child has a height specified (e.g. height: 100%), then it will ignore the align-items: stretch coming from the parent. For the stretch default to work, the child's height must compute to auto (full explanation).
Try this (no changes to HTML):
.container {
display: flex;
flex-direction: column;
height: 20em;
border: 5px solid black
}
.item {
display: flex; /* new; nested flex container */
flex: 1;
border-bottom: 1px solid white;
}
.item-inner {
display: flex; /* new; nested flex container */
flex: 1; /* new */
/* height: 100%; <-- remove; unnecessary */
/* width: 100%; <-- remove; unnecessary */
/* display: table; <-- remove; unnecessary */
}
a {
display: flex; /* new; nested flex container */
flex: 1; /* new */
align-items: center; /* new; vertically center text */
background: orange;
/* display: table-cell; <-- remove; unnecessary */
/* vertical-align: middle; <-- remove; unnecessary */
}
<div class="container">
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
</div>
jsFiddle demo
Explanation
My problem is that .item-inner { height: 100% } is not working in
webkit (Chrome).
It's not working because you're using percentage height in a way that doesn't conform with the traditional implementation of the spec.
10.5 Content height: the height property
percentage Specifies a percentage height. The percentage is calculated with respect to the height of the generated box's
containing block. If the height of the containing block is not
specified explicitly and this element is not absolutely positioned, the value computes to auto.
auto The height depends on the values of other properties.
In other words, for percentage height to work on an in-flow child, the parent must have a set height.
In your code, the top-level container has a defined height: .container { height: 20em; }
The third-level container has a defined height: .item-inner { height: 100%; }
But between them, the second-level container – .item – does not have a defined height. Webkit sees that as a missing link.
.item-inner is telling Chrome: give me height: 100%. Chrome looks to the parent (.item) for reference and responds: 100% of what? I don't see anything (ignoring the flex: 1 rule that is there). As a result, it applies height: auto (content height), in accordance with the spec.
Firefox, on the other hand, now accepts a parent's flex height as a reference for the child's percentage height. IE11 and Edge accept flex heights, as well.
Also, Chrome will accept flex-grow as an adequate parent reference if used in conjunction with flex-basis (any numerical value works (auto won't), including flex-basis: 0). As of this writing, however, this solution fails in Safari.
#outer {
display: flex;
flex-direction: column;
height: 300px;
background-color: white;
border: 1px solid red;
}
#middle {
flex-grow: 1;
flex-basis: 1px;
background-color: yellow;
}
#inner {
height: 100%;
background-color: lightgreen;
}
<div id="outer">
<div id="middle">
<div id="inner">
INNER
</div>
</div>
</div>
Four Solutions
1. Specify a height on all parent elements
A reliable cross-browser solution is to specify a height on all parent elements. This prevents missing links, which Webkit-based browsers consider a violation of the spec.
Note that min-height and max-height are not acceptable. It must be the height property.
More details here: Working with the CSS height property and percentage values
2. CSS Relative & Absolute Positioning
Apply position: relative to the parent and position: absolute to the child.
Size the child with height: 100% and width: 100%, or use the offset properties: top: 0, right: 0, bottom: 0, left: 0.
With absolute positioning, percentage height works without a specified height on the parent.
3. Remove unnecessary HTML containers (recommended)
Is there a need for two containers around button? Why not remove .item or .item-inner, or both? Although button elements sometimes fail as flex containers, they can be flex items. Consider making button a child of .container or .item, and removing gratuitous mark-up.
Here's an example:
.container {
height: 20em;
display: flex;
flex-direction: column;
border: 5px solid black
}
a {
flex: 1;
background: orange;
border-bottom: 1px solid white;
display: flex; /* nested flex container (for aligning text) */
align-items: center; /* center text vertically */
justify-content: center; /* center text horizontally */
}
<div class="container">
<a>Button</a>
<a>Button</a>
<a>Button</a>
</div>
4. Nested Flex Containers (recommended)
Get rid of percentage heights. Get rid of table properties. Get rid of vertical-align. Avoid absolute positioning. Just stick with flexbox all the way through.
Apply display: flex to the flex item (.item), making it a flex container. This automatically sets align-items: stretch, which tells the child (.item-inner) to expand the full height of the parent.
Important: Remove specified heights from flex items for this method to work. If a child has a height specified (e.g. height: 100%), then it will ignore the align-items: stretch coming from the parent. For the stretch default to work, the child's height must compute to auto (full explanation).
Try this (no changes to HTML):
.container {
display: flex;
flex-direction: column;
height: 20em;
border: 5px solid black
}
.item {
display: flex; /* new; nested flex container */
flex: 1;
border-bottom: 1px solid white;
}
.item-inner {
display: flex; /* new; nested flex container */
flex: 1; /* new */
/* height: 100%; <-- remove; unnecessary */
/* width: 100%; <-- remove; unnecessary */
/* display: table; <-- remove; unnecessary */
}
a {
display: flex; /* new; nested flex container */
flex: 1; /* new */
align-items: center; /* new; vertically center text */
background: orange;
/* display: table-cell; <-- remove; unnecessary */
/* vertical-align: middle; <-- remove; unnecessary */
}
<div class="container">
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
</div>
jsFiddle
Specifying a flex attribute to the container worked for me:
.container {
flex: 0 0 auto;
}
This ensures the height is set and doesn't grow either.
Solution: Remove height: 100% in .item-inner and add display: flex in .item
Demo: https://codepen.io/tronghiep92/pen/NvzVoo
For Mobile Safari There is a Browser fix. you need to add -webkit-box for iOS devices.
Ex.
display: flex;
display: -webkit-box;
flex-direction: column;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-webkit-flex-direction: column;
align-items: stretch;
if you're using align-items: stretch; property for parent element, remove the height : 100% from the child element.
I have had a similar issue in iOS 8, 9 and 10 and the info above couldn't fix it, however I did discover a solution after a day of working on this. Granted it won't work for everyone but in my case my items were stacked in a column and had 0 height when it should have been content height. Switching the css to be row and wrap fixed the issue. This only works if you have a single item and they are stacked but since it took me a day to find this out I thought I should share my fix!
.wrapper {
flex-direction: column; // <-- Remove this line
flex-direction: row; // <-- replace it with
flex-wrap: wrap; // <-- Add wrapping
}
.item {
width: 100%;
}
I want to have a vertical menu with a specific height.
Each child must fill the height of the parent and have middle-aligned text.
The number of children is random, so I have to work with dynamic values.
Div .container contains a random number of children (.item) that always have to fill the height of the parent. To achieve that I used flexbox.
For making links with text aligned to the middle I am using display: table-cell technique. But using table displays requires using a height 100%.
My problem is that .item-inner { height: 100% } is not working in webkit (Chrome).
Is there a fix for this problem?
Or is there a different technique to make all .item fill the height of the parent with text vertical aligned to middle?
Example here jsFiddle, should be viewed in Firefox and Chrome
.container {
height: 20em;
display: flex;
flex-direction: column;
border: 5px solid black
}
.item {
flex: 1;
border-bottom: 1px solid white;
}
.item-inner {
height: 100%;
width: 100%;
display: table;
}
a {
background: orange;
display: table-cell;
vertical-align: middle;
}
<div class="container">
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
</div>
Solution
Use nested flex containers.
Get rid of percentage heights. Get rid of table properties. Get rid of vertical-align. Avoid absolute positioning. Just stick with flexbox all the way through.
Apply display: flex to the flex item (.item), making it a flex container. This automatically sets align-items: stretch, which tells the child (.item-inner) to expand the full height of the parent.
Important: Remove specified heights from flex items for this method to work. If a child has a height specified (e.g. height: 100%), then it will ignore the align-items: stretch coming from the parent. For the stretch default to work, the child's height must compute to auto (full explanation).
Try this (no changes to HTML):
.container {
display: flex;
flex-direction: column;
height: 20em;
border: 5px solid black
}
.item {
display: flex; /* new; nested flex container */
flex: 1;
border-bottom: 1px solid white;
}
.item-inner {
display: flex; /* new; nested flex container */
flex: 1; /* new */
/* height: 100%; <-- remove; unnecessary */
/* width: 100%; <-- remove; unnecessary */
/* display: table; <-- remove; unnecessary */
}
a {
display: flex; /* new; nested flex container */
flex: 1; /* new */
align-items: center; /* new; vertically center text */
background: orange;
/* display: table-cell; <-- remove; unnecessary */
/* vertical-align: middle; <-- remove; unnecessary */
}
<div class="container">
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
</div>
jsFiddle demo
Explanation
My problem is that .item-inner { height: 100% } is not working in
webkit (Chrome).
It's not working because you're using percentage height in a way that doesn't conform with the traditional implementation of the spec.
10.5 Content height: the height property
percentage Specifies a percentage height. The percentage is calculated with respect to the height of the generated box's
containing block. If the height of the containing block is not
specified explicitly and this element is not absolutely positioned, the value computes to auto.
auto The height depends on the values of other properties.
In other words, for percentage height to work on an in-flow child, the parent must have a set height.
In your code, the top-level container has a defined height: .container { height: 20em; }
The third-level container has a defined height: .item-inner { height: 100%; }
But between them, the second-level container – .item – does not have a defined height. Webkit sees that as a missing link.
.item-inner is telling Chrome: give me height: 100%. Chrome looks to the parent (.item) for reference and responds: 100% of what? I don't see anything (ignoring the flex: 1 rule that is there). As a result, it applies height: auto (content height), in accordance with the spec.
Firefox, on the other hand, now accepts a parent's flex height as a reference for the child's percentage height. IE11 and Edge accept flex heights, as well.
Also, Chrome will accept flex-grow as an adequate parent reference if used in conjunction with flex-basis (any numerical value works (auto won't), including flex-basis: 0). As of this writing, however, this solution fails in Safari.
#outer {
display: flex;
flex-direction: column;
height: 300px;
background-color: white;
border: 1px solid red;
}
#middle {
flex-grow: 1;
flex-basis: 1px;
background-color: yellow;
}
#inner {
height: 100%;
background-color: lightgreen;
}
<div id="outer">
<div id="middle">
<div id="inner">
INNER
</div>
</div>
</div>
Four Solutions
1. Specify a height on all parent elements
A reliable cross-browser solution is to specify a height on all parent elements. This prevents missing links, which Webkit-based browsers consider a violation of the spec.
Note that min-height and max-height are not acceptable. It must be the height property.
More details here: Working with the CSS height property and percentage values
2. CSS Relative & Absolute Positioning
Apply position: relative to the parent and position: absolute to the child.
Size the child with height: 100% and width: 100%, or use the offset properties: top: 0, right: 0, bottom: 0, left: 0.
With absolute positioning, percentage height works without a specified height on the parent.
3. Remove unnecessary HTML containers (recommended)
Is there a need for two containers around button? Why not remove .item or .item-inner, or both? Although button elements sometimes fail as flex containers, they can be flex items. Consider making button a child of .container or .item, and removing gratuitous mark-up.
Here's an example:
.container {
height: 20em;
display: flex;
flex-direction: column;
border: 5px solid black
}
a {
flex: 1;
background: orange;
border-bottom: 1px solid white;
display: flex; /* nested flex container (for aligning text) */
align-items: center; /* center text vertically */
justify-content: center; /* center text horizontally */
}
<div class="container">
<a>Button</a>
<a>Button</a>
<a>Button</a>
</div>
4. Nested Flex Containers (recommended)
Get rid of percentage heights. Get rid of table properties. Get rid of vertical-align. Avoid absolute positioning. Just stick with flexbox all the way through.
Apply display: flex to the flex item (.item), making it a flex container. This automatically sets align-items: stretch, which tells the child (.item-inner) to expand the full height of the parent.
Important: Remove specified heights from flex items for this method to work. If a child has a height specified (e.g. height: 100%), then it will ignore the align-items: stretch coming from the parent. For the stretch default to work, the child's height must compute to auto (full explanation).
Try this (no changes to HTML):
.container {
display: flex;
flex-direction: column;
height: 20em;
border: 5px solid black
}
.item {
display: flex; /* new; nested flex container */
flex: 1;
border-bottom: 1px solid white;
}
.item-inner {
display: flex; /* new; nested flex container */
flex: 1; /* new */
/* height: 100%; <-- remove; unnecessary */
/* width: 100%; <-- remove; unnecessary */
/* display: table; <-- remove; unnecessary */
}
a {
display: flex; /* new; nested flex container */
flex: 1; /* new */
align-items: center; /* new; vertically center text */
background: orange;
/* display: table-cell; <-- remove; unnecessary */
/* vertical-align: middle; <-- remove; unnecessary */
}
<div class="container">
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
<div class="item">
<div class="item-inner">
<a>Button</a>
</div>
</div>
</div>
jsFiddle
Specifying a flex attribute to the container worked for me:
.container {
flex: 0 0 auto;
}
This ensures the height is set and doesn't grow either.
Solution: Remove height: 100% in .item-inner and add display: flex in .item
Demo: https://codepen.io/tronghiep92/pen/NvzVoo
For Mobile Safari There is a Browser fix. you need to add -webkit-box for iOS devices.
Ex.
display: flex;
display: -webkit-box;
flex-direction: column;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-webkit-flex-direction: column;
align-items: stretch;
if you're using align-items: stretch; property for parent element, remove the height : 100% from the child element.
I have had a similar issue in iOS 8, 9 and 10 and the info above couldn't fix it, however I did discover a solution after a day of working on this. Granted it won't work for everyone but in my case my items were stacked in a column and had 0 height when it should have been content height. Switching the css to be row and wrap fixed the issue. This only works if you have a single item and they are stacked but since it took me a day to find this out I thought I should share my fix!
.wrapper {
flex-direction: column; // <-- Remove this line
flex-direction: row; // <-- replace it with
flex-wrap: wrap; // <-- Add wrapping
}
.item {
width: 100%;
}