2

Please help me to understand one issue with the flexible box layout model for which I get different results in Firefox and Chrome.

Consider the following HTML fragment:

<body>
    <header>Header</header>
    <footer>Footer</footer>
</body>

styled via

body {
    display: -webkit-flex;
    display: flex;
    -webkit-flex-direction: column;
    flex-direction: column;
}

header {
    max-width: 400px;
    height: 20px;
    background-color: yellow;
    margin: 0 auto;
}

footer {
    width: 400px;
    height: 20px;
    background-color: green;
    margin: 0 auto;
}

The header box has a maximum width constraint of 400px while the footer has a fixed width of 400px. When I try this code in Gecko-based browsers (Firefox 21 and 24 in my case) both header and footer are horizontally centered (as I hoped for by giving them left and right auto margins) but only the footer has a width of 400px while the header's width is just the width of the content even if enough horizontal space was available.

In WebKit/Blink-based browsers (Chrome 25 and 28 in my case) the header and footers are both centered and are both 400px wide (in case there is enough horizontal space), and this is exactly what I want to achieve.

Obviously, either Firefox or Chrome must be wrong. How do you understand the spec: http://www.w3.org/TR/css3-flexbox/? What is the desired behaviour?

If you want to play around, here is a JSFiddle: http://jsfiddle.net/4Rv7K/.

Note that one has to enable the flexible box layout model in the release version of Firefox. It is the setting layout.css.flexbox.enabled. (Without it, one is actually not testing anything about flexboxes.)

P.S.: The bug was in Chromium's engine and has apparently been fixed by now: https://code.google.com/p/chromium/issues/detail?id=242654

Marc
  • 4,327
  • 4
  • 30
  • 46
  • Wasn't aware of the Firefox config setting - I've removed my answer, sorry for the trouble! – James Donnelly May 21 '13 at 14:30
  • My mistake. I have been having layout.css.flexbox.enabled enabled for so long now, that I have simply forgotten to mention it. – Marc May 21 '13 at 14:44
  • 1
    I'm inclined to believe Opera/Firefox is acting correctly in this instance. If you drop the auto margins and use align-items/align-self for centering, Chrome, Firefox, and Opera all agree: http://jsfiddle.net/4Rv7K/1/. The true answer is likely here: http://www.w3.org/TR/css3-flexbox/#cross-sizing – cimmanon May 21 '13 at 15:37
  • I have read the relevant part of the specification again. In 9.4 it says under 11 that "If a flex item has ‘align-self: stretch’, its cross size property is ‘auto’, and neither of its cross-axis margins are ‘auto’, the used outer cross size is the used cross size of its flex line, clamped according to the item's min and max cross size properties. Otherwise, the used cross size is the item's hypothetical cross size." Thus in case one sets auto margins, it is always the "otherwise" case and the "hypothetical cross size" is "fit-content" in my case. Is it this what you are saying? – Marc May 21 '13 at 18:49
  • That sounds correct (they really need an "English" translation for the rest of us so it doesn't take reading it 20 times to figure out what it means. It doesn't help that the links to definition words don't actually link to its meaning). – cimmanon May 21 '13 at 20:20
  • I am going to file a bug report for the Chrome team to investigate. If you want, you can formulate your point as an answer, which I can accept. – Marc May 21 '13 at 20:23

2 Answers2

2

The Firefox/Gecko behavior is correct.

WebKit is stretching up to 400px (the max-width) due to the header element's default "align-self: stretch" value. However, the spec is clear that "align-self: stretch" is only supposed to stretch if you have no auto margins in the cross axis. Quoting the spec:

If a flex item has ‘align-self: stretch’, [...] and neither of its cross-axis margins are ‘auto’, the used outer cross size is the used cross size of its flex line, clamped according to the item's min and max cross size properties http://www.w3.org/TR/css3-flexbox/#cross-sizing

The exception for "neither of its cross-axis margins are auto" is what Firefox is honoring here and WebKit/Blink appear to be ignoring.

Now, to achieve your desired layout: It looks like you want both stretchiness and centering, and I don't think you can get both of those things simultaneously in the cross axis.

You can get them simultaneously in the main axis of a flex container, though -- so you can fix this by adding a horizontal flex container around the header and the footer.

I've done that here: http://jsfiddle.net/4Rv7K/16/

The relevant code (just with the 'header' for simplicity):

body {
    display: -webkit-flex;
    display: flex;
    -webkit-flex-direction: column;
    flex-direction: column;
}
horizFlex {
    display: -webkit-flex;
    display: flex;
}
header {
    -webkit-flex: 1 0 auto;
    flex: 1 0 auto;
    max-width: 400px;
    height: 20px;
    background-color: yellow;
    margin: 0 auto;
}
[...]
<body><horizFlex><header>Header</header></horizFlex></body>

I think this achieves the layout you're looking for by wrapping the header and footer each in a horizontal flex container. The flex container stretches to the width of its parent (the body), and inside of it we have a single flex item (e.g. the ), which is flexible up to its max-width, and which we center (with auto margins) once it has reached its max-width.

dholbert
  • 11,386
  • 3
  • 42
  • 31
  • Actually, I set "width: 400px" for the footer to demonstrate the effect in WebKit-based browsers. For the real page, I am designing, I want the footer and header behave equally in a vertical flex container, namely that they are horizontally centered and stretch to a width up to 400px if there is enough space. (So in effect, I would like to get WebKit's behaviour in a standard-conform way.) I know how to do it with adding div's in block layout around the actual header and footer but I have been hoping for a simpler way. – Marc May 22 '13 at 07:10
  • OK. It looks like you want both stretchiness and centering, and I don't think you can get both of those things simultaneously in the cross axis. You can get them simultaneously in the main axis, though -- so if you add some horizontal flex containers, you'll be good. See updated response above. – dholbert May 22 '13 at 08:46
  • Thanks for updating your response; cimmanon mentioned below that the effect could also be achieved without extra markup by using the flex-wrap property, which, unfortunately, doesn't work with Gecko yet. Tell me in case I can help in some way to bring it to Gecko. – Marc May 22 '13 at 12:33
  • (As I replied to cimmanon, I don't think flex-wrap is really a good solution for this use-case; the layout only happens to work at a particular screen-size, and it breaks if the window gets any wider than 800px. I'm hoping to implement flex-wrap in Gecko soon, though, incidentally.) – dholbert May 23 '13 at 04:31
1

For an element that lacks a definite size with auto margins, it looks like the element's fit-content width is supposed to be used as the element's actual width while the remaining space is counted as margin. For Chrome, it appears to be behaving inappropriately only when using flex-direction: column.

http://codepen.io/cimmanon/pen/fuhyF

ul {
  display: -webkit-flex;
  display: flex;
  height: 5em;
  background: yellow;
}

li {
  margin: auto;
  border: 1px solid;
}

ul.column {
  -webkit-flex-direction: column;
  -flex-direction: column;
}

If you look at a list with the above styles, Opera, Firefox, and Chrome agree that the li elements are shrink wrapped when the direction is row. Under the column direction, only Firefox and Opera shrink wrap the li, while Chrome has the li take the full width of the flex container.

cimmanon
  • 67,211
  • 17
  • 165
  • 171
  • I think the reason why Chrome makes a difference between column and row direction is that in CSS 2.1's block layout, a vertical auto margin behaves differently to a horizontal one. – Marc May 22 '13 at 07:00
  • 1
    The element is no longer just a block element, but a flex item as well, so it has additional rules attached. Column and row orientation should have the same behavior in terms of main/cross axis behavior in regards to dimensions, behaviors, etc. It may be that what you're looking for is `flex-flow: row wrap`, but Firefox doesn't support wrapping: http://jsfiddle.net/4Rv7K/17/ – cimmanon May 22 '13 at 11:59
  • I should have been more precise: I wanted to say that the difference in Chrome may be due to a bug where relics of the block layout code made it into the flexible box layout code. But this is just a guess, I didn't take a look at the source code. – Marc May 22 '13 at 12:27
  • Your tip with flex-wrap is great. It didn't occured to me that it can be used for the layout I am looking for! Now I hope for the Gecko layout engine to catch up soon! – Marc May 22 '13 at 12:30
  • I don't think "wrap" is what you want -- it only wraps if things don't fit on the first line. Try viewing the jsfiddle in a frame that's wider than 800px (e.g. by maximizing your browser and then dragging the center-bar to shrink the "JS/HTML" half of the jsfiddle), and you'll see what I mean. – dholbert May 22 '13 at 22:36
  • That would be true if you assume the OP is presenting real code rather than a use case. Otherwise, there's lots of easier ways to get the same effect that have no thing to do with Flexbox: http://tinker.io/3da8b – cimmanon May 23 '13 at 00:36
  • I found out that the flexible box layout is not "flexible" enough to use the flex-wrap trick in my real code (which has a content area between the header and footer for which I want a flexible height). I cannot address the flex lines individually, however. – Marc May 23 '13 at 14:33
  • If all of your content needs to be centered and 400px wide or less, then there's more traditional ways of getting this effect as I mentioned above. – cimmanon May 23 '13 at 14:45