How Is Padding-Top as a Percentage Related to the Parent's Width

How is padding-top as a percentage related to the parent's width?

From CSS fluid layout: margin-top based on percentage grows when container width increases :

In CSS, all four margin: and padding: percentages are relative to the width ...even though that may seem nonsensical. That's just the way the CSS spec is, there's nothing you can do about it.

Straight from the horse's mouth:

'padding-top', 'padding-right', 'padding-bottom', 'padding-left'
Value: <padding-width> | inherit
Initial: 0
Applies to: all elements except table-row-group, table-header-group, table-footer-group, table-row, table-column-group and table-column
Inherited: no
Percentages: refer to width of containing block
Media: visual
Computed value: the percentage as specified or the absolute length

Percentages: refer to width of containing block

How is padding calculated when using percentage (%)?

TL;DR


The padding is calculated according to the parent's width


First, you should note that:

Percentage:

The percentage is calculated with respect to the width of the generated box's containing block [...] (source: w3c, emphasis mine)

This means the padding is calculated according to the parent element width (it's worth to note that in non flexbox layouts, padding top and bottom are also relative to the width of the containing block).

With box-sizing: border-box

When you change the default box-model to border-box, the padding (and border) is included in the width of the element like in your example. So with

width:600px;
padding-right:50%;
box-sizing:border-box;

The right padding must be 50% of parent's width but the overall width of element is 600px wide. The only moment the padding right is 50% of element's width is when parent width = element width (Note that this can't happen in your example because the parent is body and body has a default margin).

Workaround:

If you want the padding to be 50% of the element's width, in this box model you can:

  • set a fixed padding: width:600px; padding-right: 300px;
  • give the element a fluid width : width:50%; padding-right:25%;

Without box-sizing: border-box

In the default box-model, the padding isn't included in the width of the element so element width = 600px + 50% of parent's width as you can see in the following example:

#Test{  width:600px;  padding-right:50%;  background:#ddd;}
<div id="Test">TEXT ETISI DHOASIHD I SAIDUHSILAUDH LISH ADBHSJADB JHSA DKH ASKDH KSAH DKLJS ADLK ASJKLD KLASH DLSJAH KLS ADKL S JS KSH KD KSJ HDKJSH DKH SDKH SKDH KSJH DKJSH DKJ HTEXT ETISI DHOASIHD I SAIDUHSILAUDH LISH ADBHSJADB JHSA DKH ASKDH KSAH DKLJS ADLK ASJKLD KLASH DLSJAH KLS ADKL S JS KSH KD KSJ HDKJSH DKH SDKH SKDH KSJH DKJSH DKJ HTEXT ETISI DHOASIHD I SAIDUHSILAUDH LISH ADBHSJADB JHSA DKH ASKDH KSAH DKLJS ADLK ASJKLD KLASH DLSJAH KLS ADKL S JS KSH KD KSJ HDKJSH DKH SDKH SKDH KSJH DKJSH DKJ HTEXT ETISI DHOASIHD I SAIDUHSILAUDH LISH ADBHSJADB JHSA DKH ASKDH KSAH DKLJS ADLK ASJKLD KLASH DLSJAH KLS ADKL S JS KSH KD KSJ HDKJSH DKH SDKH SKDH KSJH DKJSH DKJ H</div>

Padding top as percentage of width not working

On MDN for padding :

Percentages refer to the width of the containing block [source]

This means percentage padding is calculated according to the width of the parent element, not the element itself.

In your case padding top for #container is calculated according to the width of <body>.

How to set the margin or padding as percentage of height of parent container?

The fix is that yes, vertical padding and margin are relative to width, but top and bottom aren't.

So just place a div inside another, and in the inner div, use something like top:50% (remember position matters if it still doesn't work)

How to calculate padding-top in % of the given div relative to its parent div

This is because padding and margin in % gets calculated from width, not height as you desire.

From the spec for padding

Unlike margin properties, values for padding values cannot be negative. Like margin properties, percentage values for padding properties refer to the width of the generated box's containing block.

And from the spec for margin

The percentage is calculated with respect to the width of the generated box's containing block. Note that this is true for 'margin-top' and 'margin-bottom' as well. If the containing block's width depends on this element, then the resulting layout is undefined in CSS 2.1.
This is also how you can create aspect-ratio locked divs in HTML since you can calculate height from width using padding-top with a percentage value.

A solution for you would be to use CSS calc, it has good browser support and fixes your issue in quite a simple manner. The only downside here is that it doesn't calculate the padding-top in % but you simply cannot calculate padding-top in % from the height of the element unless you use javascript.

If you don't absolutely require the padding-top: 2%; I would suggest the below solution.

Your CSS would look like this

ul.list li {
height: calc(10% - 8px);
padding-top: 8px;
}

This would give all ul.list li items a height of 10% - 8px and that 8px is then also used as value for padding-top which would basically make a perfect 10% again.

You can write your selectors in a different style too, I used ul.list li here as the main selector to just select all li elements but you have :nth-child(odd) and :nth-child(even) selectors.

You wrote duplicate properties on these selectors while the only difference is their background property.

I would strongly recommend to avoid duplication as much as possible as you'll definitely forget to change both properties sometimes, instead change your CSS to something like this:

/*shared properties in a general selector*/
ul.list li {
width: inherit;
height: calc(10% - 8px);
padding-top: 8px;
background: grey;
}

/*if rule is not applied, use ul.list instead of just ul*/
ul li:nth-child(even) {
background: red;
}

This even removes the need for ul li:nth-child(odd) since this can also be part of the shared styling and then be overwritten by the more specific ul li:nth-child(even) selector.

Another way

Another way of doing this instead of relying on % values is to use either em or rem values - they work by scaling based on font size.

More reading

  • web-design-basics-rem-vs-em-vs-px-sizing-elements-in-css
  • the-ems-have-it-proportional-media-queries-ftw
  • ems-rems
  • confused-rems-ems

Basically, rem is always relative to the root element which is the <html> element.

Setting a font-size on the <html> element will define the value of 1rem in CSS, if no font-size is set then the browser default (usually 16px will be used).

html {
font-size: 16px; /*1rem is now 16px*/
}

When you define the font-size like above with a value of 16px then 0.5rem will equal 8px - why is this interesting? It's scaleable! But by different means.

When you now apply a media query and change the font-size to something bigger or smaller, everything using rem will scale proportionally (you'd have to set font-size of the <html> element ofcourse.)

Now em is a bit different, it's relative to it's own font-size but since this get's inherited from it's parent element automatically (if font-size is set in em or % that is) it's basically relative to the parent.

If we take the last CSS example

html {
font-size: 16px; /* 1rem = 16px, 1em = 16px */
}

body > div { /*every direct div of body tag*/
font-size: 0.5em; /*8px*/
font-size: 0.5rem; /*8px*/
}

body > div > div { /*every second level nested div*/
font-size: 0.5em; /*4px*/
font-size: 0.5rem; /*8px*/
}

Basically, since em is sort-of relative to it's parent size the font-size decreases more when you scale in em - this is potentially dangerous since if you change your structure and add a container that slightly reduces font-size on most of the page this could have a bigger effect than you think.

With that out of the way, technically em and rem are the best ways to scale padding and margin on elements according to the font-size property.

An example for your container could look like this:

ul.list li {
height: 8%;
padding-top: 0.5rem; /*if font-size is 16px, this will be 8px*/
}

In terms of scalability this will work when you change the font-size to something else on a mobile device for instance.

It doesn't provide a solution to your question but it does provide an answer and your best alternative when talking about scalability, I hope this helps you in your quest for that :)

CSS padding-top: 100% uses width of parent?

Yes, you are correct about padding-top percent referring to the width. It is part of the W3 CSS Box model specifications:

http://www.w3.org/TR/CSS2/box.html#padding-properties

You will not be able to reference height with padding percentages. It will always refer to width. Sorry =(

How does padding-top: 100% work?

Your child div is filling parent in 100% width. Padding-top property in percent is determined by div width. So, your child is getting 100% width of parent (30%) and padding top 100% means 100% width of that element. The same applies to margin-top which is calculated by width.

Margin-top percentage not relative to parent

Vertical padding and margin are relative to the width of the parent. Top and bottom on the other hand are not.

Try to place a div inside another. Use the top property (for example: top: 25%) and make sure you specify a position (e.g. position: relative;)

I've forked and adjusted your code example to demonstrate what I mean: https://codepen.io/anon/pen/Yaqmva

top: 5%;
position: relative;

Setting child's height when parent's height is set via padding-top

use position,
padding will only add space of aspect ratio - so you need to use position:a
bsolute
to content to ignore this space and position:relative to parent to be able to position it as you intend