I've known for a long time that centring an absolutely positioned div requires writing:
top: 50%; left: 50%; translate(-50%, -50%);
but until this week I never considered how it works. I found out by accident and it's so obvious once you know. It's just that percentages confuse things and make it not obvious when you don't know.
If you absolutely position a div with
position: absolute; then it will be positioned relative to its nearest relatively positioned parent. And that position will be the top left. Which is
top: 0; and
If you want to move it down then
top: 10px; will move it down by 10px. And
top: -10px; will move it up by 10px.
top: 50%; will move it down, since it's a positive number. And the 50% refers to the size of its relatively positioned parent. Let's say that parent is 200px tall. Then 50% = 100px. So it's moving the div down by 100px.
Logically, moving a div down by half its parent's height would centre it vertically. But it's not. It's a bit too far down. The answer lies in the position it started in.
It started at the top. By which I mean that the top of the div was at the top of its parent. However much we move it down, it'll position it based on the top of the div.
top: 50%; positions the top of the div 50% of the way down its parent.
We know that to vertically centre the div
transform: translateY(-50%); works. But how?
The answer lies in what 50% is a percentage of.
Unlike with top, which was a percentage of the parent, translate is a percentage of the div itself. That 50% is moving it up by half the height of itself. Say the div is 50px tall, 50% will move it up 25px.
top: 50%; positioned the top of the div halfway down its parent? If we think about it, what we want to do to vertically centre it is to move it up by half its height, so the mid-point of the div is aligned with the mid-point of the parent.
Which is exactly what
transform: translateY(-50%); is doing.
It's obvious once you understand what those percentages are a percentage of. And easier to see, the taller your absolutely positioned div is.
It's exactly the same. The div starts off at
left: 0;: the left edge of the div is at 0px across on its parent.
left: 50%; moves it so its left edge is 50% of the way across.
transform: translateX(-50%); moves it back by 50% of the its width.
This is true in life as well as in CSS.