What does 100% mean in CSS?

Spoiler: it depends

One of the CSS units I use most is the wonderful % — so handy for positioning elements on the page.

Unfortunately, the rules aren’t exactly straightforward. One question I’m always asking myself is:

Percent of what?

Hopefully this guide can help clear things up.

#

The basics: width & height

In these examples, the purple box is our lovely self element — this is the element we’re trying to position using CSS properties. The surrounding blue box is the parent element.

Let’s start with our most basic, and most straightforward, example: width and height. Move the sliders around to get a feel for how the width and height of our self element changes with different percentage values.

height
50% (100px)
width
50% (300px)
200px
600px
.self {
position: absolute;
height: 50%;
width: 50%;
}

We can see that our element’s width and height are based on our parent’s width and height (respectively).

#

top & left

Great! Seems pretty straightforward — let’s move on to left and top:

height
50% (100px)
width
50% (300px)
top
0% (0px)
left
50% (300px)
200px
600px
.self {
position: absolute;
height: 50%;
width: 50%;
top: 0%;
left: 50%;
}

These values are also based on our parent’s width and height. If an element has a left value of 50%, its left side will sit halfway across its parent component.

#

margins

What about margins?

height
50% (100px)
width
50% (300px)
margin top
0% (0px)
margin left
50% (300px)
200px
600px
.self {
position: absolute;
height: 50%;
width: 50%;
margin-top: 0%;
margin-left: 50%;
}

margins work similarly to our last example — they are based on the size of their parent. However, there is one weird thing here that is important to note:

margin-top is based on our parent’s width, not height (and the same goes for margin-bottom). In other words, all margins are a percent of their parent's width.

#

padding

And what about padding? Should be the same as margin, right?

height
50% (100px)
width
50% (300px)
padding top
0% (0px)
padding left
50% (300px)
200px
600px
.self {
position: absolute;
height: 50%;
width: 50%;
padding-top: 0%;
padding-left: 50%;
}

And it is! For the most part.

Something interesting you might notice here is that padding-left (for example) won’t change the width of our self element, unless the padding-left value is greater than our self element’s width. This is because I use the border-box box-sizing model. Unfamiliar, or need a recap? Read more on MDN.

#

transform: translate

Okay! Here is where things get a bit... weird.

Let’s take a look at transform: translate:

height
50% (100px)
width
50% (300px)
translate top
0% (0px)
translate left
50% (150px)
200px
600px
.self {
position: absolute;
height: 50%;
width: 50%;
translate-top: 0%;
translate-left: 50%;
transform: translate(50%, 0%);
}

The box moves a lot more slowly this time, right? That’s because transform: translate percentage values are based on our self element’s width and height.

#

TLDR

Let’s recap what we’ve learned:

height
parent’s height
width
parent’s width
top
parent’s height
left
parent’s width
margin-top
parent’s width
margin-left
parent’s width
padding-top
parent’s width
padding-left
parent’s width
translate-top
self’s height
translate-left
self’s width

Now let’s put what we’ve learned to the test!

#

Centering elements

How could we center an element inside its parent, no matter its own dimensions?

Try centering our self element inside of the parent. Make sure it doesn’t move around when you tweak its width and height.

height
50% (100px)
width
50% (300px)
top
0% (0px)
left
50% (300px)
translate top
0% (0px)
translate left
50% (150px)
200px
600px
.self {
position: absolute;
height: 50%;
width: 50%;
top: 0%;
left: 50%;
translate-top: 0%;
translate-left: 50%;
transform: translate(50%, 0%);
}

See how this works? We’re moving our self element:

  • down by 50% of our parent’s height using top: 50%
  • right by 50% of our parent’s width using left: 50%
  • up by 50% of our self element’s height using transform: translate (top): -50%
  • left by 50% of our self element’s width using transform: translate (left): -50%