Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are margin/padding percentages in CSS always calculated against width?

Tags:

css

w3c

People also ask

Does padding count towards width?

The content-box model states that padding and borders don't count in the width that you set for a box. So they add on to its width .

How is padding percentage calculated?

Percentages: The padding size is relative to the width of that element's content area (i.e. the width inside, and not including, the padding, border and margin of the element). So, if your #wrapper is 940px wide, 5% padding = 0.05 × 940pixels = 47 pixels.

Why does padding affect width?

So why does this happen? This is because browsers by default add all the padding , margins , etc to the overall height and width of the element, in our case it is the div HTML element. By default, the box-sizing CSS property of the element will be set to content-box which causes this unpredictable behavior.

How does CSS calculate margin and padding?

Any margin or padding that has been specified as a percentage is calculated based on the width of the containing element. This means that padding of 5% will be equal to 5px when the parent element is 100px wide and it will be equal to 50px when the parent element is 1000px wide.


Transferring my comment to an answer, because it makes logical sense. However, please note that this is unfounded conjecture. The actual reasoning of why the spec is written this way is still, technically, unknown.

Element height is defined by the height of the children. If an element has padding-top: 10% (relative to parent height), that is going to affect the height of the parent. Since the height of the child is dependent on the height of the parent, and the height of the parent is dependent on the height of the child, we'll either have inaccurate height, or an infinite loop. Sure, this only affects the case where offset parent === parent, but still. It's an odd case that is difficult to resolve.

Update: The last couple sentences may not be entirely accurate. The height of the leaf element (child with no children) has an effect on the height of all elements above it, so this affects many different situations.


For "n%" margin (and padding) to be the same for margin-top/margin-right/margin-bottom/margin-left, all four have to be relative to the same base. If top/bottom used a different base than left/right', then "n%" margin (and padding) wouldn't mean the same thing on all four sides.

(Also note having the top/bottom margin relative to the width enables a weird CSS hack that allows you to specify a box with an unchanging aspect ratio ...even if the box is rescaled.)


I vote for the answer from @ChuckKollars after playing with this JSFiddle (on Chrome 46.0.2490.86) and referring to this post (written in Chinese).


A major reason against the infinite calculation conjecture is that: using width faces the same infinite calculation problem.

Have a look at this JSFiddle, the parent display is inline-block, which is eligible to define margin/padding on it. The child has margin value 20%. If we follow the infinite calculation conjecture:

  1. The width of the child depends on the parent
  2. The width of the parent depends on the child

But as a result, Chrome stops the calculation somewhere, resulting:

enter image description here

If you try to expand the "result" panel horizontally on the JSFiddle, you will find that the width of them will not change. Please note that the content in the child is wrapped into two lines (not, say, one line), why? I guess Chrome just hard-code it somewhere. If you edit the child content to make it more (JSFiddle), you will find that as long as there is extra space horizontally, Chrome keeps the content two lines.

So we can see: there is some way to prevent the infinite calculation.


I agree with the conjecture that: this design is just to keep the four margin/padding values based on the same measure.

this post (written in Chinese) also proposes another reason is that: it is because of the orientation of reading/typeset. We read from top to down, with the width fixed and height infinite (virtually).


I realize the OP is asking why the CSS specification defines top/bottom margin percentages as a % of width (and not, as would be assumed, height), but I thought it might also be useful to post a potential solution.

Most modern browsers support vw and vh now which lets you specify margin numbers against the viewport width and viewport height.

100vw/100vh equals 100% width/100% height (respectively) if there's no scrollbar; if there is a scrollbar the viewport numbers don't account for this (while the % numbers do). Thankfully, nearly all browsers use scrollbar sizes of 17px (see here), so you can use css calc function to account for this. If you don't know whether a scrollbar will appear or not, then this solution will not work.

For example: Assuming no horizontal scrollbar, a top margin of 50% of height, could be defined as "margin-top: 50vh;". With a horizontal scrollbar, this could be defined as "margin-top: calc(0.5 * (100vh - 17px));" (remember that the minus and plus operators in calc require spaces on both sides!).


I know this question is a bit old, but I'd like to refresh it for CSS3. While it's true that the CSS2.1 specification says that percentage padding and margin are defined relative to the width of the containing block, this is not always the case. It depends on the writing mode. This comes straight from the CSS3 specs:

As a corollary, percentages on the margin and padding properties, which are always calculated with respect to the containing block width in CSS2.1, are calculated with respect to the inline size of the containing block in CSS3.

I cover this in my tutorial on aspect ratios with CSS.

Specifically, there's a section on Percentage Padding in Horizontal vs. Vertical Writing Modes. By default, an element has a horizontal writing mode, where text flows horizontally (in the "inline" direction) from left to right. However, using the writing-mode CSS property, you can actually set the mode to be vertical (with text either flowing from right to left or left to right). Here are some diagrams of horizontal vs vertical writing modes:

A horizontal writing mode, with text flowing vertically from top to bottom. An arrow points from left to right at the top of the document and is labeled as the inline direction. Another arrow points from top to bottom and is labeled as the block direction.

A vertical writing mode, with text flowing horizontally. The horizontal axis is labeled as the block direction, whereas the vertical axis is now labeled as the inline direction. Text is rendered sideways.

These are taken from the MDN docs on writing modes.

In vertical writing modes, percentage padding will be relative to the height of the containing block, not to the width.

Here's proof:

.document {
  writing-mode: vertical-rl;
  width: 100%;
  height: 100vh;
}

.parent {
   width: 100%;
   height: 200px;
   background-color: black;
   color: white;
}

.child {
  padding: 10%;
  background-color: white;
  color: black;
  border: solid 1px;
}
<div class="document">
  <div class="parent">
    <div class="child">
      Child
    </div>
  </div>
</div>

The child gets 20px of padding, which is 10% of its containing block's height (200px).

As to the why in the question, this was covered well in the other posts here.