flex-direction seems to break horizontal margin auto in Safari 10 - css

I was trying to use flex-direction column along with margin: auto for a child element in Safari 10, but the horizontal margins don't seem to apply while they do in literally every other browser (Edge, Firefox, Chrome). Here's a quick repro:
http://jsfiddle.net/paostj73/
.center-pls {
display: flex;
height: inherit;
flex-direction: column;
}
.to-center {
margin: auto;
}
It looks like this in Safari:
And like this in Chrome:
I know that setting align-items is a workaround, but unfortunately, it also happens to break other parts of my layout. Is there another way to fix this, and is this already a known issue?

This is an issue IE has, and if I'm not mistaken Safari too, caused by the align-items default value stretch, which stretch the element to full width and fail to apply the auto margin.
To keep the default, align-items: stretch for all items but the to-center element, add align-self: center to its rule
html, body {
height: 100%;
margin: 0;
}
.center-pls {
display: flex;
height: inherit;
flex-direction: column;
}
.to-center {
margin: auto;
align-self: center; /* added, fix for IE and Safari */
}
<div class="center-pls">
<div class="top-nav">
aaa aaa aa
</div>
<div class="to-center">
aaaa
</div>
</div>
Based on how the other elements are suppose to render, setting align-items: flex-start to the center-pls, will also work, though then the other elements might need width: 100% set, so they stretch and fill their parent's width.
html, body {
height: 100%;
margin: 0;
}
.center-pls {
display: flex;
height: inherit;
flex-direction: column;
align-items: flex-start; /* added, fix for IE and Safari */
}
.to-center {
margin: auto;
}
<div class="center-pls">
<div class="top-nav">
aaa aaa aa
</div>
<div class="to-center">
aaaa
</div>
</div>

Related

inline-flex ignores parent's padding when inline-block doesn't

I am making a grid using css flexbox. The issue I am facing is that when I changed grid's display from inline-block to inline-flex the children no longer respect padding-right. Isn't inline-flex supposed to behave the same as inline-block in this respect, and if so what explains this difference? The reason I want to switch to inline-flex is because I want children (whose display is set to inline-block) to line up vertically. I am also curious as to why overflow-y impacts behavior.
body { margin: 0; }
/* wrapper sets the overall height. We use flex to make contents fill height. */
.wrapper {
height: 300px;
display: flex;
flex-direction: column;
}
/* grid can have header and body. header is omitted for now. */
.grid {
flex: 1 1 0; /* this makes grid same height as wrapper */
display: inline-flex;
flex-direction: column; /* lays out contents vertically */
overflow-y: auto; /* overflow-y strangely impacts whether padding is honored */
padding-left: 15px;
padding-right: 15px;
}
.grid-body {
margin-top: 50px;
margin-bottom: 50px;
display: inline-block; /* makes width the same as row */
}
.row {
width: 1420px;
height: 30px;
}
<div class="wrapper">
<div class="grid">
<div class="grid-body">
<div class="row" style="background-color:#de7;"></div>
<div class="row" style="background-color:#7ed;"></div>
</div>
</div>
</div>

align-self: stretch not working in Safari? [duplicate]

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%;
}

Flex auto margin not working in IE10/11

I have a complex layout where I center various elements vertically and horizontally with flexbox.
The last element then has margin-right:auto; applied to push the elements left (and negate centering them).
This works correctly everywhere except on IE10/11 and has been driving me crazy.
HTML/CSS sample:
#container {
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-ms-flex-flow: row wrap;
-webkit-flex-flow: row wrap;
flex-flow: row wrap;
-ms-flex-pack: center;
-webkit-justify-content: center;
justify-content: center;
-ms-flex-align: center;
-webkit-align-items: center;
align-items: center;
-ms-flex-line-pack: center;
-webkit-align-content: center;
align-content: center;
}
#second-item {
margin-right: auto;
}
/* just some colors - not important */
#container {
height: 200px;
width: 100%;
background: red;
}
#container > div {
background: blue;
padding: 10px;
outline: 1px solid yellow;
}
<div id='container'>
<div id='first-item'>first item</div>
<div id='second-item'>second item</div>
</div>
http://codepen.io/anon/pen/NrWVbR
You'll see two items on the screen that should be left-aligned on the side of the red parent (i.e. they should both be centered, but the last item has margin-right:auto; applied and is filling the entire line, pushing the other item and itself on the side) - this is the correct behaviour. Except in IE10/11 where both items are incorrectly centered i.e. the second item's margin-right:auto; is ignored.
Any IE/flexbox experts out there that have encountered something like this before?
This appears to be an IE bug.
According to the flexbox specification:
8.1. Aligning with auto margins
Prior to alignment via justify-content and align-self, any positive free space is distributed to auto margins in that dimension.
Note: If free space is distributed to auto margins, the alignment properties will have no effect in that dimension because the margins will have stolen all the free space left over after flexing.
In other words, auto margins take precedence over justify-content.
In fact, if an element has auto margins applied, then keyword alignment properties such as justify-content and align-self have no effect (because the auto margins have taken all the space).
Your code works as expected in Chrome and Firefox because those browsers are in compliance with the spec.
IE10 and IE11 appear to not be in compliance. They are not applying the auto margin as defined in the spec.
(Note that IE10 is built on a previous version of the spec.)
Solutions
Method #1: Use auto margins only
If justify-content is removed, auto margins work fine in IE10/11.
So don't use justify-content. Use auto margins all the way through. (See examples of alignment with auto margins).
Method #2: Use an invisible spacer div
Create a third div with visibility: hidden and flex-grow:1. This will naturally shift #first-item and #second-item to the left edge, with no need for auto margins.
#container {
display: flex;
flex-flow: row wrap;
justify-content: center;
align-items: center;
align-content: center;
}
#third-item {
flex-grow: 1;
visibility: hidden;
}
/* just some colors - not important */
#container {
height: 200px;
width: 100%;
background: pink;
}
#container > div {
background: cornflowerblue;
padding: 10px;
outline: 1px solid yellow;
}
<div id='container'>
<div id='first-item'>first item</div>
<div id='second-item'>second item</div>
<div id='third-item'>third item</div>
</div>

How to get the same margin behavior in ie11? [duplicate]

I have a complex layout where I center various elements vertically and horizontally with flexbox.
The last element then has margin-right:auto; applied to push the elements left (and negate centering them).
This works correctly everywhere except on IE10/11 and has been driving me crazy.
HTML/CSS sample:
#container {
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-ms-flex-flow: row wrap;
-webkit-flex-flow: row wrap;
flex-flow: row wrap;
-ms-flex-pack: center;
-webkit-justify-content: center;
justify-content: center;
-ms-flex-align: center;
-webkit-align-items: center;
align-items: center;
-ms-flex-line-pack: center;
-webkit-align-content: center;
align-content: center;
}
#second-item {
margin-right: auto;
}
/* just some colors - not important */
#container {
height: 200px;
width: 100%;
background: red;
}
#container > div {
background: blue;
padding: 10px;
outline: 1px solid yellow;
}
<div id='container'>
<div id='first-item'>first item</div>
<div id='second-item'>second item</div>
</div>
http://codepen.io/anon/pen/NrWVbR
You'll see two items on the screen that should be left-aligned on the side of the red parent (i.e. they should both be centered, but the last item has margin-right:auto; applied and is filling the entire line, pushing the other item and itself on the side) - this is the correct behaviour. Except in IE10/11 where both items are incorrectly centered i.e. the second item's margin-right:auto; is ignored.
Any IE/flexbox experts out there that have encountered something like this before?
This appears to be an IE bug.
According to the flexbox specification:
8.1. Aligning with auto margins
Prior to alignment via justify-content and align-self, any positive free space is distributed to auto margins in that dimension.
Note: If free space is distributed to auto margins, the alignment properties will have no effect in that dimension because the margins will have stolen all the free space left over after flexing.
In other words, auto margins take precedence over justify-content.
In fact, if an element has auto margins applied, then keyword alignment properties such as justify-content and align-self have no effect (because the auto margins have taken all the space).
Your code works as expected in Chrome and Firefox because those browsers are in compliance with the spec.
IE10 and IE11 appear to not be in compliance. They are not applying the auto margin as defined in the spec.
(Note that IE10 is built on a previous version of the spec.)
Solutions
Method #1: Use auto margins only
If justify-content is removed, auto margins work fine in IE10/11.
So don't use justify-content. Use auto margins all the way through. (See examples of alignment with auto margins).
Method #2: Use an invisible spacer div
Create a third div with visibility: hidden and flex-grow:1. This will naturally shift #first-item and #second-item to the left edge, with no need for auto margins.
#container {
display: flex;
flex-flow: row wrap;
justify-content: center;
align-items: center;
align-content: center;
}
#third-item {
flex-grow: 1;
visibility: hidden;
}
/* just some colors - not important */
#container {
height: 200px;
width: 100%;
background: pink;
}
#container > div {
background: cornflowerblue;
padding: 10px;
outline: 1px solid yellow;
}
<div id='container'>
<div id='first-item'>first item</div>
<div id='second-item'>second item</div>
<div id='third-item'>third item</div>
</div>

Images loosing their aspect ratio when they are displayed as flex-items in Firefox

I've just come along a strange bug in Firefox. I have an image displayed as a flex-item and want it to shrink down to a maximum size using max-width and height: auto. It works perfectly in all browsers I tested, except Firefox. I guess it has something to do with the default alignment of flex-items which is stretch. I overwrote it to flex-start, which worked fine, but not in Firefox.
Has anybody else experienced this?
img {
max-width: 50px;
height: auto;
}
.container {
display: flex;
align-items: flex-start;
}
See this Codepen for an example: http://codepen.io/kaesetoast/pen/PPevbv
Use flex-basis insread of max-width
img {
flex-basis: 50px;
height: auto;
}
img {
flex-basis: 50px;
height: auto;
}
.container {
display: flex;
align-items: flex-start;
}
<div class="container">
<img src="http://lorempixel.com/1440/1500/" width="1440" height="1500" alt="">
</div>
Tested in FF 41.0.2

Resources