I tried putting a <div> below an <img> with a negative margin, but the <img> ended up covering the <div>. I am confused because the <img> is not positioned, and is not creating a stacking context, so there seems to have no reason for the <img> to go against the natural rendering order of HTML.
When I used display: flex, everything worked as expected, which makes me even more confused. Can someone explain this to me? Are there any specs that outline this behavior?
.img {
display: block;
}
.square {
height: 100px;
margin-top: -30px;
background-color: green;
}
#flex {
display: flex;
flex-direction: column;
}
#container {
display: flex;
gap: 20px;
}
#container > * {
width: 100px;
}
<div id="container">
<div>
<img class="img" src="https://placehold.co/100">
<div class="square"></div>
</div>
<div id="flex">
<img class="img" src="https://placehold.co/100">
<div class="square"></div>
</div>
</div>
>Solution :
Both behaviors are correct. In a normal flow (your first example), the content always have the priority over the background of adjacent elements so the image will get painted above the background.
Here is a relevant question with full detail: Why the content is not covered by the background of an overlapping element?
When you make the container a flexbox one, the painting algorithm is modified because flex items behave differently
Flex items paint exactly the same as inline blocks ref
inline block elements are painted as a whole element like the ones that create stacking context so no more interaction with the content of sibling elements
For inline-block and inline-table elements:
1 For each one of these, treat the element as if it created a new stacking context, ref
Here is another example with text content on the second div to better understand the first case:
.img {
display: block;
}
.square {
height: 100px;
margin-top: -30px;
background-color: green;
}
#flex {
display: flex;
flex-direction: column;
}
#container {
display: flex;
gap: 20px;
}
#container > * {
width: 100px;
}
<div id="container">
<div>
<img class="img" src="https://placehold.co/100">
<div class="square"> text content</div>
</div>
<div id="flex">
<img class="img" src="https://placehold.co/100">
<div class="square"> text content</div>
</div>
</div>
The text is always painted above the image and only the background is behind. By making the container a flexbox one, you change this behavior.