Vertical Percentages in CSS

Vertical Percentages in CSSI don’t think this happens very often. To be honest, I don’t think I’ve ever used a percentage value on a top padding/margin, or a bottom padding/margin value. Even for height values, I may have used a 100% value for a hack, but nothing else.

Nonetheless, using percentages on those vertical parts of a page are somewhat different in the way they are calculated, compared to other properties that take percentage values.

Here are the rules for percentages on vertical items:

  • A percentage value on top/bottom padding or margins is relative to the width of the containing block
  • A percentage value on height is relative to the height of the containing block (same for min/max height)
  • a Percentage value on top and bottom values for a positioned element is relative to the height of the containing block (hat tip to Hashem Qolami for the reminder)
  • Percentage values are not allowed on border widths (not even left and right)

And here’s a CodePen showing this in action, using top/bottom padding/margins set with percentages, along with a set-width container:

See the Pen Vertical Percentages by Louis Lazaris (@impressivewebs) on CodePen

Notice it uses jQuery to calculate and display the total height of the element including padding. I’ve intentionally used a value of 1000px for the container and 100×100 for the element itself, just to make any percentage calculations simpler to envision.

As it stands, the total height is 300px — which is 100px (as defined in the CSS) plus 100px of top padding (which is 10% of 1000px of the container width) and 100px of bottom padding (same calculation as top).

And keep in mind that the calculations would be vastly different if I just threw some content in the boxes and let the container element expand without a set width. Then the container’s width would be dependent on the window size (or iframe size, in the case of a code embed like above).

Also, if I was using box-sizing: border-box, that would affect the total height value that’s displayed.

Why Relative to Container Width?

In thinking about why vertical padding and margin percentage values are relative to the containing block’s width, my guess would be that this is simply much easier to control.

Width values are commonly set, whereas heights are usually just dependent on content. In fact, I would go as far as to say it’s usually bad practice to define a height on an element (min-height is fine though). So if you want to actually set vertical margin/padding percentages, it would be unintuitive to set these relative to an arbitrary content-based height. Instead, it’s much easier to do the calculation based on width — because it’s much more likely that a width is being set explicitly.

Of course, I don’t know if this is the case. I can’t seem to find an explanation for this anywhere, so if anyone who works on the specs has a more technical answer, then please comment and I’ll update the article accordingly.

These links on my CSS Values reference have external links to MDN and W3C, so you can look those up to see what the spec says:

Advertise Here

18 Responses

  1. The first two list bullets can be combined into one:

    «A percentage value on top/bottom padding or margin is relative to the width of the containing block»

    DRY FTW!

  2. Navaru:

    Thank you!

  3. If vertical paddings and margins will be respective to containig box height then there will not be appropriate way to calculate height of containing box. If vertical paddings and vertical margins will grow then height of containing box will grow and vice versa.

  4. Thierry’s tweet adds some good info to this:

    I had forgotten about that method, and it’s quite relevant to this discussion.

  5. Thanks Louis for this topic; I expected you to cover top and bottom properties for positioned elements as well.

    • That is a good point. That’s interesting, as there seems to be some quirky behaviour on that. I might post a follow-up on this after I research it. Thanks!

      Edit: I’ve updated it. Looks like a simple line added to the “rules” list is enough. One thing I noticed is that if you use a min-height value on the container element, the top/bottom positioning value will not work with a percentage. But it will work with a height value (which is not great).

      • Hi Louis. Thanks for the update.
        But about the min-height issue, that’s weird, because it (% on top/bottom) works even you’ve used min-height on the container; At least in my test case: http://jsbin.com/imOWAnI/1

        • This is actually getting interesting, because I think I’ve tracked down a bug in one or more browsers.

          What you showed is true, but don’t forget that top/bottom values work on relatively positioned elements, regardless of positioning contexts. Here is a fork of your example:

          http://jsbin.com/axAkAQol/1/edit

          Notice the min-height value, and the percentage for the “top” property on the child, which isn’t working. Notice also there is no “relative” context on the parent, and the child is positioned relatively, not absolute. If you change the min-height to “height”, you’ll see that the “top” value will then take effect.

          This behaviour is present in latest stable Chrome (and Canary), latest stable Firefox, IE10, and Safari (all tested on OSX and Windows).

          Strangely, the only browser that has what seems to be the correct behaviour is Opera 12.16 (latest stable, still using the Presto engine, not Blink).

          Maybe there’s something about positioning and percentages that I’m not understanding here, but it would seem that the percentage value should still take effect in that example, but it doesn’t. Opera seems to be the only one doing it right. Any thoughts on this?

          After I post this comment, I’m going to submit a bug report with Chrome and FF and see if someone has a response.

        • It’s a known bug on Chrome:

          https://code.google.com/p/chromium/issues/detail?id=72416

          There is a very old similar bug report on this for Firefox that was labelled ‘invalid’:

          https://bugzilla.mozilla.org/show_bug.cgi?id=2992

          which someone has tried to have re-opened at some point, but I decided to create a new one:

          https://bugzilla.mozilla.org/show_bug.cgi?id=939641

          If Opera’s behaviour is wrong, then I guess it won’t matter. They’re using WebKit/Blink now so eventually all browsers will treat this the same way.

          • I searched the spec to find something have explained this behavior, but no success! there are some rules about top, min-height calculation for positioned and/or none-positioned elements.
            But none of them have mentioned that using these properties at the same time would have what behavior in different contexts.

            Just found another bug report here:
            https://bugzilla.mozilla.org/show_bug.cgi?id=260348

            The issue is using absolute positioning on the child, doesn’t need the parent to have specified height; But for relative positioned elements, it does :|

  6. Even if I never thought about setting padding or margin top/bottom in % this is kinda interesting. Thanks for your post and the codepen.

  7. I worked with that a while ago for styling day CSS Day Logo (http://www.netzaffin.de/really-the-cssday-logo-shouldnt-be-an-image/)

    Yep, I was really confused until Eric Meyer refered me to the specs.

  8. Hi,
    Very new concept. but easy to do.Thanks for this technique.

    Thanks

  9. Cool trick. It’s one of the trick that I’m sure not lots of designer are familiar with.

Leave a Reply

Comment Rules: Please use a real name or alias. Keywords are not allowed in the "name" field. If you use keywords, your comment will be deleted, or your name will be replaced with the alias from your email address. No foul language, please. Thank you for cooperating.

Instructions for code snippets: Wrap inline code in <code> tags; wrap blocks of code in <pre> and <code> tags. When you want your HTML to display on the page in a code snippet inside of <code> tags, make sure you use &lt; and &gt; instead of < and >, otherwise your code will be eaten by pink unicorns.