4

I stumbled upon an issue with image centering within a flexbox with direction:column.

Imagine you have two elements within a flexbox, where the first one contains an image:

<div class="container">
    <div class="image-container">
        <img class="img" src="https://interactive-examples.mdn.mozilla.net/media/examples/firefox-logo.svg">
    </div>
    <div class="another-flex-child">
        Random content here
    </div>
</div>


.container {
  height: 300px;
  background-color: green;
  display: flex;
  flex-direction: column;

  .image-container {
    flex: 1;
    align-self: center;

    .img {
      height: 100%;
    }
  }

  .another-flex-child {
    flex: none;
    background-color: red;
  }
}

I would expect the image to be center horizontally within the div, but it appears the left border of the image is exactly at the center of the div.

When I replace the image with another div which contains some text it is placed as expected.

Can anybody explain to me whats happening there?

Checkout this fiddle

Tobi
  • 2,001
  • 2
  • 27
  • 49

3 Answers3

3

Because your <div> that contains the image (and has align-self: center on it) is by default a block-level element, and has a width of 100% by default. As such, it is constrained in relation to the parent.

In order to have your image centered correctly, you'll want to add display: contents with:

container .image-container {
  display: inline;
}

This can be seen in the following:

.container {
  height: 300px;
  background-color: green;
  display: flex;
  flex-direction: column;
}

.container .image-container {
  flex: 1;
  align-self: center;
  display: contents;
}

.container .image-container .img {
  height: 100%;
}

.another-flex-child {
  flex: none;
  background-color: red;
}

.spacer {
  height: 20px;
}
<div class="container">
  <div class="image-container">
    <img class="img" src="https://interactive-examples.mdn.mozilla.net/media/examples/firefox-logo.svg">
  </div>
  <div class="another-flex-child">
    Random content here
  </div>
</div>

<div class="spacer"></div>

<div class="container">
  <div class="image-container">
    <div>Properly centered content</div>
  </div>
  <div class="another-flex-child">
    Random content here
  </div>
</div>
Obsidian Age
  • 41,205
  • 10
  • 48
  • 71
  • well also only adding "overflow: auto;" on image-container makes everything looks as expected – Alberto Sinigaglia Nov 17 '19 at 20:45
  • *is by default a block-level element, and has a width of 100% by default. As such, it is constrained in relation to the parent.* --> what you mean by this? that div is a flex item and doesn't have width:100% also your image isn't really centred by it's stretched to fill all the container (add some border to see this) – Temani Afif Nov 17 '19 at 20:50
  • @TemaniAfif is in fact right about that - accepting his answer... – Tobi Nov 17 '19 at 21:08
2

The issue is that you are using an SVG with no intrinsic dimension and only an intrinsic ratio so it's like your image has a width equal to 0 which make its centred container with a width equal to 0, too.

Here is before using height:100%

.container {
  height: 300px;
  background-color: green;
  display: flex;
  flex-direction: column;
}

.image-container {
  flex: 1;
  align-self: center;
  border:2px solid blue;
}

.img {
  /*height: 100%;*/
}

.another-flex-child {
  flex: none;
  background-color: red;
}

.spacer {
  height: 20px;
}
<div class="container">
  <div class="image-container">
    <img class="img" src="https://interactive-examples.mdn.mozilla.net/media/examples/firefox-logo.svg">
  </div>
  <div class="another-flex-child">
    Random content here
  </div>
</div>

<div class="spacer"></div>

<div class="container">
  <div class="image-container">
    <div>Properly centered content</div>
  </div>
  <div class="another-flex-child">
    Random content here
  </div>
</div>

After setting height:100% the image will fill all the space and will keep its ratio but you will have an overflow because the browser will not go back to calculate the width of the container again:

.container {
  height: 300px;
  background-color: green;
  display: flex;
  flex-direction: column;
}

.image-container {
  flex: 1;
  align-self: center;
  border:2px solid blue;
}

.img {
  height: 100%;
}

.another-flex-child {
  flex: none;
  background-color: red;
}

.spacer {
  height: 20px;
}
<div class="container">
  <div class="image-container">
    <img class="img" src="https://interactive-examples.mdn.mozilla.net/media/examples/firefox-logo.svg" >
  </div>
  <div class="another-flex-child">
    Random content here
  </div>
</div>

<div class="spacer"></div>

<div class="container">
  <div class="image-container">
    <div>Properly centered content</div>
  </div>
  <div class="another-flex-child">
    Random content here
  </div>
</div>

To avoid this give the image a width and make sure to add min-height:0 to the container to allow it to shrink

.container {
  height: 300px;
  background-color: green;
  display: flex;
  flex-direction: column;
}

.image-container {
  flex: 1;
  align-self: center;
  border:2px solid blue;
  min-height:0;
}

.img {
  height: 100%;
}

.another-flex-child {
  flex: none;
  background-color: red;
}

.spacer {
  height: 20px;
}
<div class="container">
  <div class="image-container">
    <img class="img" src="https://interactive-examples.mdn.mozilla.net/media/examples/firefox-logo.svg" width="250">
  </div>
  <div class="another-flex-child">
    Random content here
  </div>
</div>

<div class="spacer"></div>

<div class="container">
  <div class="image-container">
    <div>Properly centered content</div>
  </div>
  <div class="another-flex-child">
    Random content here
  </div>
</div>

If you were initially using an image with intrinsic dimension you won't have this issue and you don't need to define a width. You will only need to add min-height:0 to avoid the overflow:

.container {
  height: 300px;
  background-color: green;
  display: flex;
  flex-direction: column;
}

.image-container {
  flex: 1;
  align-self: center;
  border:2px solid blue;
  min-height:0;
}

.img {
  height: 100%;
}

.another-flex-child {
  flex: none;
  background-color: red;
}

.spacer {
  height: 20px;
}
<div class="container">
  <div class="image-container">
    <img class="img" src="https://picsum.photos/id/1/400/400">
  </div>
  <div class="another-flex-child">
    Random content here
  </div>
</div>

<div class="spacer"></div>

<div class="container">
  <div class="image-container">
    <div>Properly centered content</div>
  </div>
  <div class="another-flex-child">
    Random content here
  </div>
</div>

Note that the above doesn't work the same way in Firefox and you will need to add text-aling:center to make sure it works the same everywhere:

.container {
  height: 300px;
  background-color: green;
  display: flex;
  flex-direction: column;
}

.image-container {
  flex: 1;
  align-self: center;
  border:2px solid blue;
  text-align:center;
  min-height:0;
}

.img {
  height: 100%;
}

.another-flex-child {
  flex: none;
  background-color: red;
}

.spacer {
  height: 20px;
}
<div class="container">
  <div class="image-container">
    <img class="img" src="https://picsum.photos/id/1/400/400">
  </div>
  <div class="another-flex-child">
    Random content here
  </div>
</div>

<div class="spacer"></div>

<div class="container">
  <div class="image-container">
    <div>Properly centered content</div>
  </div>
  <div class="another-flex-child">
    Random content here
  </div>
</div>

You will notice that the difference is related to the width calculation of the container which a bit complex due to the use of height:100%

Things may get worse if the size of the image is very small:

.container {
  height: 300px;
  background-color: green;
  display: flex;
  flex-direction: column;
}

.image-container {
  flex: 1;
  align-self: center;
  border:2px solid blue;
  text-align:center;
  min-height:0;
}

.img {
  height: 100%;
}

.another-flex-child {
  flex: none;
  background-color: red;
}

.spacer {
  height: 20px;
}
<div class="container">
  <div class="image-container">
    <img class="img" src="https://picsum.photos/id/1/50/50">
  </div>
  <div class="another-flex-child">
    Random content here
  </div>
</div>

<div class="spacer"></div>

<div class="container">
  <div class="image-container">
    <div>Properly centered content</div>
  </div>
  <div class="another-flex-child">
    Random content here
  </div>
</div>

In Firefox text-align:center will do nothing and you may need a nested flexbox container

.container {
  height: 300px;
  background-color: green;
  display: flex;
  flex-direction: column;
}

.image-container {
  flex: 1;
  align-self: center;
  justify-content:center;
  border:2px solid blue;
  display:flex;
  min-height:0;
}

.img {
  height: 100%;
}

.another-flex-child {
  flex: none;
  background-color: red;
}

.spacer {
  height: 20px;
}
<div class="container">
  <div class="image-container">
    <img class="img" src="https://picsum.photos/id/1/50/50">
  </div>
  <div class="another-flex-child">
    Random content here
  </div>
</div>

<div class="spacer"></div>

<div class="container">
  <div class="image-container">
    <div>Properly centered content</div>
  </div>
  <div class="another-flex-child">
    Random content here
  </div>
</div>

The below is almost the same issue you were having with the initial SVG that can fixed with this same code but it won't remove the overflow:

.container {
  height: 300px;
  background-color: green;
  display: flex;
  flex-direction: column;
}

.image-container {
  flex: 1;
  align-self: center;
  display:flex;
  justify-content:center;
  border:2px solid blue;
}

.img {
  height: 100%;
}

.another-flex-child {
  flex: none;
  background-color: red;
}

.spacer {
  height: 20px;
}
<div class="container">
  <div class="image-container">
    <img class="img" src="https://interactive-examples.mdn.mozilla.net/media/examples/firefox-logo.svg" >
  </div>
  <div class="another-flex-child">
    Random content here
  </div>
</div>

<div class="spacer"></div>

<div class="container">
  <div class="image-container">
    <div>Properly centered content</div>
  </div>
  <div class="another-flex-child">
    Random content here
  </div>
</div>

Another intresting thing to note is that your initial code may work fine if you add height:100% to the container making the calculation of the nested height easier:

.container {
  height: 300px;
  background-color: green;
  display: flex;
  flex-direction: column;
}

.image-container {
  flex: 1;
  align-self: center;
  border:2px solid blue;
  box-sizing:border-box;
  height:100%;
}

.img {
  height: 100%;
  display:block;
}

.another-flex-child {
  flex: none;
  background-color: red;
}

.spacer {
  height: 20px;
}
<div class="container">
  <div class="image-container">
    <img class="img" src="https://interactive-examples.mdn.mozilla.net/media/examples/firefox-logo.svg" >
  </div>
  <div class="another-flex-child">
    Random content here
  </div>
</div>

<div class="spacer"></div>

<div class="container">
  <div class="image-container">
    <div>Properly centered content</div>
  </div>
  <div class="another-flex-child">
    Random content here
  </div>
</div>
Temani Afif
  • 245,468
  • 26
  • 309
  • 415
  • I thought that the issue might be related to svg dimensions, but I saw the same problem with a png... – Tobi Nov 17 '19 at 20:50
  • @Tobi no, you want have issue with a PNG or an image with intrinsic width/height. Example: https://jsfiddle.net/f58c2zj1/ – Temani Afif Nov 17 '19 at 20:53
  • Hmm, you are right...Just wondering what I actually tried in my manic experiments while trying to get it to work ;) – Tobi Nov 17 '19 at 20:54
  • But seems your answer is a very viable solution, although I haven't fully gotten it yet – Tobi Nov 17 '19 at 20:55
  • @Tobi with no intrinsic dimension your element will have a default width equal to 0 . that's why the container is shrinking .. if you later add height:100% you only make the image grow and overflow but not the container because the browser doesn't go back to avoid a cycle in the calculation – Temani Afif Nov 17 '19 at 20:56
  • Great answer!! Would give you more than one upvote if I could :) I noticed the difference between Firefox and Chrome and was wondering if that was an nonconformity in firefox. I don't do frontend too often but I am really amazed what can be done since Flex arrived. That being said, I probably could have solved my issue with JS in 10 minutes, but the curiosity how it should be done always prevails ;) – Tobi Nov 17 '19 at 22:39
  • 1
    @Tobi sometimes the calculation is a bit tricky and difficult to figure out ... I have updated again to add another solution at the end, still tricky to explain but will also fix your issue – Temani Afif Nov 17 '19 at 22:45
-1

Add the justify-content like below:

.image-container {
    flex: 1;
    align-self: center;
    justify-content:center;
}

it should works

lisarko8077
  • 299
  • 5
  • 23