How top and translate work together to centre a div


3 min read

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.

An absolutely positioned div

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 left: 0;.

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.

Moving the div by 50%

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.

Why isn't it centred?

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.

So top: 50%; positions the top of the div 50% of the way down its parent.

How does transform: translate move it up?

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.

Remember how 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.

What about horizontal centring?

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.

Always ask what is it a percentage of

This is true in life as well as in CSS.