9

I have a flex container which contains an img flex item.

I want this img to disregard its intrinsic height (90px) so that the height may shrink to match the sibling's flex item height (as per the default align-items: stretch style).

.container {
    border: 1px solid;
    display: flex;
}

.item {
    border: 1px solid;
}

.content {
    width: 200px;
    height: 50px;
    background-color: hotPink;
}
<div class="container">
    <img class="item" src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRu-3yBSd2b6JCOMcGVSOFf8X49QB3Ik-OI87gKEMwWLrdJxP5qOErmZQ">
    <div class="item">
        <div class="content"></div>
    </div>
</div>

We can see the desired behaviour if we swap the img for a div:

.container {
    border: 1px solid;
    display: flex;
}

.item {
    border: 1px solid;
}

.dynamicHeightContent {
    width: 120px;
    height: 100%;
    background-color: green;
}

.content {
    width: 200px;
    height: 50px;
    background-color: hotPink;
}
<div class="container">
    <div class="item">
        <div class="dynamicHeightContent"></div>
    </div>
    <div class="item">
        <div class="content"></div>
    </div>
</div>

I have tried min-height: 0 on the img, to no avail.

  • What is the special behaviour that applies to img but not div?
  • How can we opt out of img's special behaviour so that it behaves like other flex items (such as div)? If there isn't a way to opt-out, is there a recommended workaround?

Note that whilst the img won't shrink its height, it does appear to grow:

.container {
    border: 1px solid;
    display: flex;
}

.item {
    border: 1px solid;
}

.content {
    width: 200px;
    height: 300px;
    background-color: hotPink;
}
<div class="container">
    <img class="item" src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRu-3yBSd2b6JCOMcGVSOFf8X49QB3Ik-OI87gKEMwWLrdJxP5qOErmZQ">
    <div class="item">
        <div class="content"></div>
    </div>
</div>

Note: I'm happy to disregard the img's aspect ratio. I plan to avoid skewing the img via object-fit: cover.

kukkuz
  • 41,512
  • 6
  • 59
  • 95
Oliver Joseph Ash
  • 3,138
  • 2
  • 27
  • 47

3 Answers3

3

Note that in your example, the item is the flex item and not content - you should check the strech behaviour of item here.


How can we opt out of img's special behaviour so that it behaves like other flex items (such as div)?

It behaves like other flex items - <img> may not be very useful as a flex item but the stretch behaviour works fine:

  • if the image has more height than item, the item stretches to the height of the image
  • if the image has less height than item, the image stretches to the height of the content breaking its aspect ratio.

See demo below:

.container {
  border: 1px solid;
  display: flex;
}

.item {
  background: cadetblue;
}

.content {
  width: 200px;
  background-color: hotPink;
}

img {
  display: block;
}
<h2>Small content</h2>
<div class="container">
  <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRu-3yBSd2b6JCOMcGVSOFf8X49QB3Ik-OI87gKEMwWLrdJxP5qOErmZQ">
  <div class="item">
    <div class="content">some text here some text here some text here </div>
  </div>
</div>
<br/>
<h2>Large Content</h2>
<div class="container">
  <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRu-3yBSd2b6JCOMcGVSOFf8X49QB3Ik-OI87gKEMwWLrdJxP5qOErmZQ">
  <div class="item">
    <div class="content">some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here </div>
  </div>
</div>

I have tried min-height: 0 on the img, to no avail.

The min-height: 0 is given to a column flexbox along the flex direction to override the default auto behaviour (for row direction the property is min-width) - this doesn't work in the cross axis.

You can see details and some examples of this below:


Wrapping image in a container

Now wrap the <img> in a div and see the same situation - the stretch behaviour is again good:

.container {
  border: 1px solid;
  display: flex;
}

.item {
  background: cadetblue;
}

.content {
  width: 200px;
  background-color: hotPink;
}

img {
  display: block;
}
<h2>Small content</h2>
<div class="container">
  <div class="item">
    <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRu-3yBSd2b6JCOMcGVSOFf8X49QB3Ik-OI87gKEMwWLrdJxP5qOErmZQ"></div>
  <div class="item">
    <div class="content">some text here some text here some text here </div>
  </div>
</div>
<br/>
<h2>Large Content</h2>
<div class="container">
  <div class="item">
    <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRu-3yBSd2b6JCOMcGVSOFf8X49QB3Ik-OI87gKEMwWLrdJxP5qOErmZQ"></div>
  <div class="item">
    <div class="content">some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here </div>
  </div>
</div>

The difference now is that you can use object-fit successfully on the image now (this does not work properlly if it is a flex item):

.container {
  border: 1px solid;
  display: flex;
}

.item {
  background: cadetblue;
}

.content {
  width: 200px;
  background-color: hotPink;
}

img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
<h2>Small content</h2>
<div class="container">
  <div class="item">
    <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRu-3yBSd2b6JCOMcGVSOFf8X49QB3Ik-OI87gKEMwWLrdJxP5qOErmZQ"></div>
  <div class="item">
    <div class="content">some text here some text here some text here </div>
  </div>
</div>
<br/>
<h2>Large Content</h2>
<div class="container">
  <div class="item">
    <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRu-3yBSd2b6JCOMcGVSOFf8X49QB3Ik-OI87gKEMwWLrdJxP5qOErmZQ"></div>
  <div class="item">
    <div class="content">some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here </div>
  </div>
</div>

I want this img to disregard its intrinsic height (90px) so that the height may shrink to match the sibling's flex item height

The only way to disregard the image height is to use positioning on the image wrapper:

  • position the image absolutely with respect to its wrapper,
  • you can either give a width to the image wrapper or give flex: 1 on item to get half of the available width horizontally.

See demo below:

.container {
  border: 1px solid;
  display: flex;
}

.item {
  background: cadetblue;
  flex: 1; /* equal width for items */
}

.content {
  width: 200px;
  background-color: hotPink;
}

.item:first-child { /* image container */
  position: relative;
}

img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  position: absolute; /* position absolutely */
  top: 0;
  left: 0;
}
<h2>Small content</h2>
<div class="container">
  <div class="item">
    <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRu-3yBSd2b6JCOMcGVSOFf8X49QB3Ik-OI87gKEMwWLrdJxP5qOErmZQ"></div>
  <div class="item">
    <div class="content">some text here some text here some text here </div>
  </div>
</div>
<br/>
<h2>Large Content</h2>
<div class="container">
  <div class="item">
    <img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRu-3yBSd2b6JCOMcGVSOFf8X49QB3Ik-OI87gKEMwWLrdJxP5qOErmZQ"></div>
  <div class="item">
    <div class="content">some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here some text here </div>
  </div>
</div>
kukkuz
  • 41,512
  • 6
  • 59
  • 95
  • 1
    That makes sense, thank you. It's a shame that the `position: absolute` trick is necessary. I suspect this will be a common issue developers run into. It feels like we need a declarative way to do this added to CSS, so we don't have to resort to these hacks. I'll be watching to see how much attention this question gets :-) – Oliver Joseph Ash Apr 17 '19 at 10:29
1

I think you are looking at this the wrong way.

The image itself is 90px high which means the content of the flex item has a height of 90px(because your image is the flex item). So your div on the right side is matching the image height because it is higher than the div's height and not the other way around.

Think of it as if you have set the height of the flex item(image) to 90px. So it won't shrink pass it unless you set it to a smaller height. Even though you didn't explicitly set the image height to 90px but it is 90px naturally so it is implicitly set which causes the confusion. So while it will stretch beyond 90px in height it won't go under it because that's the height of the flex item content.

In the case where you swapped it for a div and it works like you imagined is because there is no height set on the .item div which is the flex item.

Huangism
  • 16,278
  • 7
  • 48
  • 74
  • That makes sense, but I would have expected a way to ignore the image's intrinsic size/height, e.g. `min-height: 0`. Separately, what is the recommended workaround? This is a very common use case. – Oliver Joseph Ash Apr 11 '19 at 17:46
  • Again, the image's dimensions act as the content of the flex item, think of the image as a block of text and this text takes up 90px height of space. If you want it to be smaller, you will have to set a height. – Huangism Apr 11 '19 at 17:52
  • The thing about the image is that it's one solid block that's 90px high, like one letter that's 90px high so it cannot shrink shorter than that because that's the smallest it can go – Huangism Apr 11 '19 at 18:00
  • That's a nice way of seeing it. But when you use `object-fit`, the image can work at any height. For that reason it might make sense for CSS to have some mechanism for ignoring the intrinsic height. – Oliver Joseph Ash Apr 11 '19 at 18:07
  • I hope explanation makes sense, it's the only way I can logically explain this – Huangism Apr 11 '19 at 18:09
0

while this is not the answer you are asking, but the div and img actually behave the same, that is if there is content taller than the height, it grows the height

example:

.container {
    border: 1px solid;
    display: flex;
}

.item {
    border: 1px solid;
}

.dynamicHeightContent {
    width: 120px;
    height: 100%;
    background-color: green;
}

.content {
    width: 200px;
    height: 50px;
    background-color: hotPink;
}
<div class="container">
    <div class="item">
        <div class="dynamicHeightContent">
        akhsdjahskd<br>
        ajkhsdkjhasd<br>
        ajshdkahsd<br>
        ajsgdjgad<br>
        ajshdasjdh<br>
        </div>
    </div>
    <div class="item">
        <div class="content"></div>
    </div>
</div>

since you insist that object-fit should solve it, you can try this, as you can see, object-fit only works if we set the height and width, otherwise the browser will try to make it "keep aspect ratio", just try to remove the height or width from the css (one at a time), you will see this effect

.container {
    border: 1px solid;
    display: flex;
}

.item {
    border: 1px solid;
}

img.item {
    height: 50px;
    width: 200px;
    object-fit: cover;
}

.content {
    width: 200px;
    height: 50px;
    background-color: hotPink;
}
<div class="container">
    <img class="item" src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRu-3yBSd2b6JCOMcGVSOFf8X49QB3Ik-OI87gKEMwWLrdJxP5qOErmZQ">
    <div class="item">
        <div class="content"></div>
    </div>
</div>
am05mhz
  • 2,727
  • 2
  • 23
  • 37