Have you ever failed to understand how the percentages work in CSS? Ever wondered why it's so messed up and has zero logic sometimes? Well, I do. That's why I'm writing this post to share my understanding through my researches and readings with you.
Percentage of what?
As a percentage, obviously there should be a target taken as a reference source. Most answers to this are the parent block of the element we assign the percentage. This is correct, but does not entirely cover all the cases. The most correct answer should be the containing block, meaning the block that contains our element and it doesn't have to be the direct parent.
Let's take a look at the below example:
In this example, I created 3 nested div
s, which are 3 squares with the following characteristics:
- The outmost grandparent div has a lightgray color with a size of 4x4
- The parent div has a darker gray with a size of 2x2
- And the red child div which I assigned the size of 50%
If the percentage unit took the parent as the source, the size of the child should have been 1/2 of it, but no, the child actually has the size equals to the parent and 1/2 of the grandparent as you can see. The reason is the grandparent div is the true containing block of the child div, due to the fact that the child has position: absolute
, corresponding to position: relative
I have set in the grandparent.
Therefore, to identify which is the actual containing block of an element, it's entirely based on the position
property of the element itself. You can read more on MDN.
However, for certain properties, the reference source for the percentage unit is neither the parent nor the containing block, instead, it is itself - self element.
Percentage by property
width
/height
Pretty straightforward, as you have seen in the example above, when an element is assigned a percentage value to its width
, the width
of the containing block is taken as the reference source. Likewise, height
of the element refers to the height
of the containing block.
padding
For padding
, either vertical (padding-top
/padding-bottom
) or horizontal (padding-left
/padding-right
) refers to the width
of the containing block.
Example:
In this example,
- The parent div has a size of 6x4.
- The child div has a size of 0, but with
padding-top
andpadding-left
given 50%
The result is that the child has a size equivalent to 1/2 width
of the parent, which is a 3x3 square.
margin
Similar to padding
, the percentage of margin
(both vertical and horizontal) refers to the width
of the containing block.
Example:
In this example,
- The parent div has a size of 6x4.
- The child div with
margin-top
andmargin-left
given 50%
The result is that the child is positioned 3 units away from the top and left margins of the parent (1/2 width
of the parent).
top
/bottom
/left
/right
For these properties (usually come with position
), the vertical ones (top
/bottom
) refer to the height
and the horizontal ones (left
/right
) refer to the width
of the containing block.
Example:
In this example,
- The parent div has a size of 6x4.
- The child div has
position: absolute
withtop
andleft
given 50%
The result is that the child div is positioned 2 units away from the parent's top edge (1/2 height
of the parent), and positioned 3 units away from the parent's left edge (1/2 width
of the parent).
transform: translate()
An incredible property for animation/transition, it also supports percentage value. However, this one does not refer to its containing block, but instead refers to itself.
Example:
In this example,
- The parent div has a size of 6x4.
- The child div has a size of 2x1 with
transform: translate(50%, 50%)
The result is that the child div is positioned 0.5 unit away from the parent's top edge (1/2 height
of itself), and positioned 1 unit away from the parent's left edge (1/2 width
of itself).
background-size
The background-size
property brings the complexity of percentage unit to a new level 😄
The percentage value of this property now refers to the background positioning area, which I interpret as similar to the containing block, but with an addition of these 3 factors:
- Block with only content (
content-box
) - Block with content and padding (
padding-box
) - Block with content, padding and border (
border-box
)
The 3 factors are given by the background-origin
property. You can read more on MDN.
Example:
In this example,
- The parent div has a size of 6x4.
- The child div has a size of 3x2, no
padding
, noborder
- I used a DEV logo (with the ratio of a square 1:1) as a
background-image
for the child div, with thebackground-size
property set to 50% 50%
The result is that the background image has been stretched to have a size of 1.5x1, corresponding to 1/2 size of the child.
background-position
Similar to background-size
, the percentage of the background-position
property also relies on the background positioning area.
Example:
In this example, the same image and layout was used as the previous. As we changed the value of background-position
, some observations we can see:
- Without any value (by default, the value is
0 0
), the background image is positioned at the top left corner. - With
background-position: 0 50%
, the background image is positioned left center. - With
background-position: 50% 50%
, the background image is positioned in the center. - With
background-position: 100% 100%
, the background image is positioned right bottom.
Note:
background-position: 0 50%
is equivalent to:
background-position-x: 0
background-position-y: 50%
Apparently, there are some calculations behind the percentage of this property, instead of just the distance between the image's top and left edges to the child's. Through some researching and testing, it appears that the background-position
property relies on the following calculation before yielding an actual value:
offset X = (container's width - image's width) * background-position-x
offset Y = (container's height - image's height) * background-position-y
In this case, with
- container as the child div
-
image's width/height is the resulting size of
background-size
font-size
For font-size
, the percentage value solely refers to its direct parent block.
Example:
In this example, I use the same layout as the very first example, with the font-size
assigned as below:
- 13px for grandparent
- 26px for parent
- 50% for child
As a result, we can clearly see that the font-size
of the child is now equivalent to the grandparent and is 1/2 of the parent, ignoring the fact that the position: relative
is assigned to the grandparent, not the parent.
line-height
While might not be as popular, I also mention this property as it also supports percentage. The percentage value of line-height
relies on the font-size
of itself.
Example:
In this example,
- The paragraph has 11 lines
-
font-size
is set 20px -
line-height
is set 150%
The actual height
of the whole block is ~329px,
- The
line-height
in this case is: 20 * 150% = 30px. - The
height
is then: 30 * 11 = 330px, approximate to the actualheight
.
Takeaways
Hope the article cleared up some of your understanding regarding the percentage value in CSS instead of making things worse 😅
I also tweeted a cheatsheet to summarize what is written so far, maybe it will come in handy to remember the meanings at first:
And this is a collection of all the examples in the article: https://codepen.io/collection/xKwgdW