Wowee this is some strange behaviour.
It looks like the margin-top
on the p
is flowing out the top of the grid but the grid is calculating it’s own height based on including that margin, so that makes it look like the p
has a margin-bottom
that’s 2em
instead of 1em
(it’s not though).

It’s weird behaviour but I think it’s related to the way that margins collapse with adjacent elements.
We can confirm this by adding display: inline-block
to the p
element (which prevents it from collapsing margins).
Inline Block

So now that we know what’s happening, what to do about it?
If you can live with the display: inline-block
on the p
then that’ll work. Otherwise you could kill the margin (p { margin: 0 }
) and replace it with padding.
Padding instead of Margin

The first child and last child selectors can be useful for this.
For example: Removing margins
.grid {
display: grid;
}
.card > div {
border-left: 1px solid black;
height: 100%;
}
.card > div :first-child {
margin-top: 0;
}
.card > div :last-child {
margin-bottom: 0;
}
<div class="grid">
<a class="card" href="https://example.com">
<div>
<p>
Hello Stackoverflow! There is no margin below me!
</p>
</div>
</a>
</div>
Or: Fake margins with padding
.grid {
display: grid;
}
.card > div {
border-left: 1px solid black;
height: 100%;
}
.card > div :first-child {
margin-top: 0;
padding-top: 1em;
}
.card > div :last-child {
margin-bottom: 0
padding-bottom: 1em;
}
<div class="grid">
<a class="card" href="https://example.com">
<div>
<p>
Hello Stackoverflow! There is 1em of "margin" below me!
</p>
</div>
</a>
</div>
I consider myself to be a fairly experienced CSS user, but this problem has stumped me. I have a basic CSS setup that I would expect to work in a predictable way.
.grid {
display: grid;
}
.card > div {
border-left: 1px solid black;
height: 100%
}
<div class="grid">
<a class="card" href="https://example.com">
<div>
<p>
Hello Stackoverflow! There is 2em of margin below me!
</p>
</div>
</a>
</div>
Note the interesting behavior where the bottom margin of the p
appears to be 2em
instead of the following reasonable expectations
1em
on top, 1em
on bottom
0em
This caught me off guard, so I messed with the values a bit. This is where it gets really interesting
- remove
display: grid
: 2em
on bottom drops to 0
- remove
height: 100%
: 2em
on the bottom drops to 0
- change
border-left
to border
: 1em
on top, 1em
on bottom
When we remove the a
and adjust our classes to match that (inner div becomes .card
)
- Patterns are normal but still confusing
- Usually
1em
on top, 1em
on bottom
border-left
-> border
: changes nothing
height: 100%
: changes nothing
- removing
display: grid
: makes margin 0
What the heck is going on here? The inspect tool reveals nothing, if anything it makes the issue more confusing. The div
is offset from the anchor by 1em
, but it’s not clear why
Ideally, the margin is 0
(w/o display: grid
style), but I can settle for an even 1em
on both sides. I’m mostly curious why this behavior happens and what I can do to negate it.
Thank you wizards!
Thank you. While this answer does not fully explain why this happens (it gets close!) and it is still unclear if this is intentional or just a bug, it did lead me in the right direction. Future readers: I suggest messing with