An image in a flexbox that has a max-height
style appears to render differently depending on whether it has its height
and width
attributes set.
The one with the attributes, set to the true width/height of the image, renders with its aspect ratio preserved, but the ones without the attributes respects the max-height
and appear squashed.
.flex-parent {
display: flex;
max-height: 10vh;
}
<div class="flex-parent">
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/b/be/Red_eyed_tree_frog_edit2.jpg/320px-Red_eyed_tree_frog_edit2.jpg">
<img width="320" height="240" src="https://upload.wikimedia.org/wikipedia/commons/thumb/b/be/Red_eyed_tree_frog_edit2.jpg/320px-Red_eyed_tree_frog_edit2.jpg">
</div>
This is how is appears in Chrome 58 (and it appears similarly in Firefox 54).
Why do they render differently? What are the rules that govern this behaviour?
My (obviously incorrect) understanding is that height and width attributes overwrite the intrinsic height and width that is found when the image loads, and if the height and width attributes equal the dimensions of the image, there should be no difference in rendering once the image has loaded.
The context is making a page with responsive images, where each image
vh
in the CSS)The frog image is from https://en.wikipedia.org/wiki/File:Red_eyed_tree_frog_edit2.jpg
The width or height of a flex container or flex item, whichever is in the cross dimension, is that box's cross size. Its cross size property is thus either its width or height property, whichever is in the cross dimension.
Flex-basis is both width and height in a Flexbox, depending on the flex direction.
The flex-basis Property on the Block Axis On the block axis ( flex-direction: column ), flex-basis takes the height of the flex-items into account, instead of their width. In this case, the flex-basis value prevails over the height value, just like with the width value on the inline axis.
The flex-basis is the thing that sizing is calculated from. If we set flex-basis to 0 and flex-grow to 1 then all of our boxes have no starting width, so the space in the flex container is shared out evenly, assigning the same amount of space to each item.
An initial setting of a flex container is align-items: stretch
.
That means that flex items will expand across the cross axis of the container.
In a row-direction container, like in the question, the cross axis is vertical.
That means the items (images, in this case) will cover the full height of the container.
However, when a flex item has a defined cross size, that overrides the stretch
default.
From the spec:
8.3. Cross-axis Alignment: the
align-items
andalign-self
propertiesFlex items can be aligned in the cross axis of the current line of the flex container, similar to
justify-content
but in the perpendicular direction.
stretch
If the cross size property of the flex item computes to
auto
, and neither of the cross-axis margins areauto
, the flex item is stretched.
This is the key language:
If the cross size property of the flex item computes to
auto
And this is how the spec defines "cross size property":
The width or height of a flex item, whichever is in the cross dimension, is the item’s cross size. The cross size property is whichever of
width
orheight
that is in the cross dimension.https://www.w3.org/TR/css-flexbox-1/#cross-size-property
So your code appears to be playing out as defined in the spec.
This is what you have:
.flex-parent {
display: flex;
max-height: 10vh;
}
<div class="flex-parent">
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/b/be/Red_eyed_tree_frog_edit2.jpg/320px-Red_eyed_tree_frog_edit2.jpg">
<img width="320" height="240" src="https://upload.wikimedia.org/wikipedia/commons/thumb/b/be/Red_eyed_tree_frog_edit2.jpg/320px-Red_eyed_tree_frog_edit2.jpg">
</div>
The first image has no defined width (main size) or height (cross size), either in the CSS or the HTML. Its cross size, therefore, computes to auto
.
This statement:
If the cross size property of the flex item computes to
auto
... is true, and align-items: stretch
goes into effect.
The image expands across the height of the container, which is 10px.
The second image, however, has explicit sizing. The cross size is defined in the HTML (height="240"
).
Now this statement:
If the cross size property of the flex item computes to
auto
... is false, and align-items: stretch
is overridden. The HTML height
attribute prevails.
Two notes about the second image:
max-height
limit on the container because an initial setting in CSS is overflow: visible
.width
and height
attributes map to their respective CSS properties. Hence, height="240"
overrides the height: auto
default. See below for the spec references.*There are two other issues to consider when rendering images in a flex container.
The default minimum size of flex items. This setting prevents flex items from shrinking below the size of their content. Read this post for full details:
Varying behavior among major browsers. Firefox, Chrome, Safari, Edge and IE11 don't always render images as flex items in the same way. Read this post for more details:
Considering all factors above, here's my suggested solution:
.flex-parent {
display: flex;
max-height: 50vh; /* adjusted for demo */
}
.flex-parent {
min-width: 0;
}
img {
width: 100%;
height: auto;
}
<div class="flex-parent">
<div>
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/b/be/Red_eyed_tree_frog_edit2.jpg/320px-Red_eyed_tree_frog_edit2.jpg">
</div>
<div>
<img width="320" height="240" src="https://upload.wikimedia.org/wikipedia/commons/thumb/b/be/Red_eyed_tree_frog_edit2.jpg/320px-Red_eyed_tree_frog_edit2.jpg">
</div>
</div>
*The HTML width
and height
attributes map to their respective CSS properties. Hence, when specified, they override the CSS defaults. From the HTML5 spec:
10.4.3 Attributes for embedded content and images
The
width
andheight
attributes onapplet
,embed
,iframe
,object
orvideo
elements, andinput
elements with atype
attribute in the Image Button state and that either represents an image or that the user expects will eventually represent an image, map to the dimension propertieswidth
andheight
on the element respectively.10.2 The CSS user agent style sheet and presentational hints
When the text below says that an attribute on an element maps to the dimension property (or properties), it means that if the element has an attribute set, and parsing that attribute's value using the rules for parsing dimension values doesn't generate an error, then the user agent is expected to use the parsed dimension as the value for a presentational hint for properties, with the value given as a pixel length if the dimension was an integer, and with the value given as a percentage if the dimension was a percentage.
Tried reading the w3c flexbox specs and stumbled onto this. It says that
For each dimension, if that dimension of the flex container’s content box is a definite size, use that; if that dimension of the flex container is being sized under a min or max-content constraint, the available space in that dimension is that constraint.
Otherwise, subtract the flex container’s margin, border, and padding from the space available to the flex container in that dimension and use that value which might result in an infinite value.
Hope this helps.
Here you have written max-height:10vh, vh does not support in any browsers except the very latest iOS 6. This goes for all the viewport related length units. use other values instead of Vh this will work fine. Please Read this blog https://css-tricks.com/the-lengths-of-css/.
.flex-parent {
display: flex;
max-height: max-content;
}
<div class="flex-parent">
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/b/be/Red_eyed_tree_frog_edit2.jpg/320px-Red_eyed_tree_frog_edit2.jpg">
<img width="320" height="240" src="https://upload.wikimedia.org/wikipedia/commons/thumb/b/be/Red_eyed_tree_frog_edit2.jpg/320px-Red_eyed_tree_frog_edit2.jpg">
</div>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With