282

How come a percentage value for height doesn’t work but a percentage value for width does?

For example:

<div id="working"></div>
<div id="not-working"></div>
#working{
    width:80%;
    height:140px;
    background:orange;
}
#not-working{
    width:80%;
    height:30%;
    background:green;
}

The width of #working ends up being 80% of the viewport, but the height of #not-working ends up being 0.

easwee
  • 15,757
  • 24
  • 60
  • 83
Web_Designer
  • 72,308
  • 93
  • 206
  • 262
  • 4
    Not an expert, but I'd imagine it has something to do with the assumption that you're going to be scrolling up and down a web page, but not sideways... –  Apr 14 '11 at 02:46
  • 2
    Here's a simple and clear explanation of the CSS `height` property with percentage values: http://stackoverflow.com/a/31728799/3597276 – Michael Benjamin Aug 25 '15 at 20:10

6 Answers6

424

The height of a block element defaults to the height of the block's content. So, given something like this:

<div id="outer">
    <div id="inner">
        <p>Where is pancakes house?</p>
    </div>
</div>

#inner will grow to be tall enough to contain the paragraph and #outer will grow to be tall enough to contain #inner.

When you specify the height or width as a percentage, that's a percentage with respect to the element's parent. In the case of width, all block elements are, unless specified otherwise, as wide as their parent all the way back up to <html>; so, the width of a block element is independent of its content and saying width: 50% yields a well defined number of pixels.

However, the height of a block element depends on its content unless you specify a specific height. So there is feedback between the parent and child where height is concerned and saying height: 50% doesn't yield a well defined value unless you break the feedback loop by giving the parent element a specific height.

mu is too short
  • 426,620
  • 70
  • 833
  • 800
  • 54
    correct and well explained. the difference between them is that width by default is defined by the value of its parents element, whereas heigth is define by the "value" of its content. parent's value vs content's value – arthur Jan 11 '13 at 09:57
  • 7
    so is there no way to have height that is responsive? i.e. - you have a row of colored squares created with css background color and you want the squares to resize with the screen. [jjsFiddle](http://jsfiddle.net/MilkyTech/A2bU7/6/) – MilkyTech May 27 '14 at 23:08
  • @ChrisM: I can't think of anything in pure CSS off the top of my head but I haven't been doing CSS lately. Maybe try asking a new question. – mu is too short May 27 '14 at 23:39
  • i thought I might need to ask a new q. I've been experimenting and can't get anything to work. think I may need js – MilkyTech May 28 '14 at 00:11
  • 2
    @ChrisM - Depending on how you want to use it, just set equal paddings to half the width you want the box: http://jsfiddle.net/en9xyv30/1/ If you want content in the box, it'll likely need to be absolutely positioned -- just set top, right, bottom, and left all to 0 (or the same value other than 0 if you want some padding) and the content will resize along with the box. – Josh Coady Aug 27 '14 at 05:00
  • 13
    You can also use the vw unit. vw is a unit that is a percent of the viewport width. So, something like height: 10vw; width: 10vw; would scale to screen width and remain square. See http://caniuse.com/#feat=viewport-units for browser compatibility. – Josh Coady Aug 27 '14 at 05:11
  • The element is similar to a
    in that it's height is content dependent. Consequently, in order for an outermost
    percentage height to have any meaning, the height of the element must be specified.
    – George Dec 18 '14 at 17:06
  • 5
    Be careful with replacing percentages with `vw` unit as it will make your life really hard if you decide to put the element into another container. – Tomasz Mularczyk Dec 16 '16 at 18:16
  • Then why this example width not create infinete loop? https://jsfiddle.net/8dkzp49w/16/ @arthur – Murad Sofiyev Jul 23 '20 at 10:11
172

A percentage value in a height property has a little complication, and the width and height properties actually behave differently to each other. Let me take you on a tour through the specs.

height property:

Let's have a look at what CSS Snapshot 2010 spec says about height:

The percentage is calculated with respect to the height of the generated box's containing block. If the height of the containing block is not specified explicitly (i.e., it depends on content height), and this element is not absolutely positioned, the value computes to 'auto'. A percentage height on the root element is relative to the initial containing block. Note: For absolutely positioned elements whose containing block is based on a block-level element, the percentage is calculated with respect to the height of the padding box of that element.

OK, let's take that apart step by step:

The percentage is calculated with respect to the height of the generated box's containing block.

What's a containing block? It's a bit complicated, but for a normal element in the default static position, it's:

the nearest block container ancestor box

or in English, its parent box. (It's well worth knowing what it would be for fixed and absolute positions as well, but I'm ignoring that to keep this answer short.)

So take these two examples:

<div   id="a"  style="width: 100px; height: 200px; background-color: orange">
  <div id="aa" style="width: 100px; height: 50%;   background-color: blue"></div>
</div>

<div   id="b"  style="width: 100px;              background-color: orange">
  <div id="bb" style="width: 100px; height: 50%; background-color: blue"></div>
</div>

In this example, the containing block of #aa is #a, and so on for #b and #bb. So far, so good.

The next sentence of the spec for height is the complication I mentioned in the introduction to this answer:

If the height of the containing block is not specified explicitly (i.e., it depends on content height), and this element is not absolutely positioned, the value computes to 'auto'.

Aha! Whether the height of the containing block has been specified explicitly matters!

  • 50% of height:200px is 100px in the case of #aa
  • But 50% of height:auto is auto, which is 0px in the case of #bb since there is no content for auto to expand to

As the spec says, it also matters whether the containing block has been absolutely positioned or not, but let's move on to width.

width property:

So does it work the same way for width? Let's take a look at the spec:

The percentage is calculated with respect to the width of the generated box's containing block.

Take a look at these familiar examples, tweaked from the previous to vary width instead of height:

<div   id="c"  style="width: 200px; height: 100px; background-color: orange">
  <div id="cc" style="width: 50%;   height: 100px; background-color: blue"></div>
</div>

<div   id="d"  style="            height: 100px; background-color: orange">
  <div id="dd" style="width: 50%; height: 100px; background-color: blue"></div>
</div>
  • 50% of width:200px is 100px in the case of #cc
  • 50% of width:auto is 50% of whatever width:auto ends up being, unlike height, there is no special rule that treats this case differently.

Now, here's the tricky bit: auto means different things, depending partly on whether its been specified for width or height! For height, it just meant the height needed to fit the contents*, but for width, auto is actually more complicated. You can see from the code snippet that's in this case it ended up being the width of the viewport.

What does the spec say about the auto value for width?

The width depends on the values of other properties. See the sections below.

Wahey, that's not helpful. To save you the trouble, I've found you the relevant section to our use-case, titled "calculating widths and margins", subtitled "block-level, non-replaced elements in normal flow":

The following constraints must hold among the used values of the other properties:

'margin-left' + 'border-left-width' + 'padding-left' + 'width' + 'padding-right' + 'border-right-width' + 'margin-right' = width of containing block

OK, so width plus the relevant margin, border and padding borders must all add up to the width of the containing block (not descendents the way height works). Just one more spec sentence:

If 'width' is set to 'auto', any other 'auto' values become '0' and 'width' follows from the resulting equality.

Aha! So in this case, 50% of width:auto is 50% of the viewport. Hopefully everything finally makes sense now!


Footnotes

* At least, as far it matters in this case. spec All right, everything only kind of makes sense now.

Flimm
  • 136,138
  • 45
  • 251
  • 267
  • 7
    +10 would give this a "+10" if it were possible. – Henry Oct 05 '15 at 09:06
  • Fantastic explanation - thank you! I added some letters in box id='b' so that it actualy has a hight. Still the height of box id='bb' is 0 - why? Shouldnt it be half of the content height of box id='b'? – Adam Jan 30 '16 at 22:08
  • @Adam For height, #bb's height is 50% of auto, which is the same as auto, and since #bb has nothing in it, it's the same as 0. The fact that that #b the parent ends up having a height doesn't change the fact that its height is auto. – Flimm Feb 05 '16 at 19:47
  • "If 'width' is set to 'auto', any other 'auto' values become '0' and 'width' follows from the resulting equality." ..... i'm really confused at this sentence, can you clarify? – carinlynchin Jul 11 '16 at 13:13
  • 1
    @Flimm could you explain why setting `%` height on child of the flex items works even when there is no height specified explicitly on parent? I mean, when you have flex container, flex items and inner div inside flex item - when you set height in `%` on that inner div it inherits height from the flex child (it doesn't matter that flex item's height is "set" to `auto`). – max Nov 28 '17 at 00:11
  • The chapter "Basic Visual Formatting" of the book CSS The Definitive Guide also goes through this, I recommend a read. – Flimm Apr 05 '18 at 14:39
  • Wow, Very well explained! Totally +1 – jmsandiegoo Nov 14 '19 at 06:57
  • What about html = height:100%, body = height:100% #wrapper = min-height:100%. It does not work although #rapper is a direct child of body. What the hell it wrong with whole CSS? – Čamo May 07 '21 at 17:38
  • Very Nice and Detailed Explanation and height and width. +10 if that is possible on voting – Ajay Kopperla Nov 22 '22 at 10:39
  • @Čamo for the height if we dont set the height in pixel at html or body in your case wont work as it is resulted as auto mean height of the content – Ajay Kopperla Nov 22 '22 at 10:41
19

I think you just need to give it a parent container... even if that container's height is defined in percentage. This seams to work just fine: JSFiddle

html, body { 
    margin: 0; 
    padding: 0; 
    width: 100%; 
    height: 100%;
}
.wrapper { 
    width: 100%; 
    height: 100%; 
}
.container { 
    width: 100%; 
    height: 50%; 
}
David Martins
  • 1,830
  • 6
  • 22
  • 30
  • You can also do this to subtract pixels if you have a header for example, or the % will likely be calculated wrong when you resize the window. height: calc(100% - 100px); And for older browsers, use this: height: -o-calc(100% - 100px); /* opera */ height: -webkit-calc(100% - 100px); /* google, safari */ height: -moz-calc(100% - 100px); /* firefox */ – Paintoshi May 26 '17 at 11:27
8

Another option is to add style to div

<div style="position: absolute; height:somePercentage%; overflow:auto(or other overflow value)">
 //to be scrolled 
</div>

And it means that an element is positioned relative to the nearest positioned ancestor.

Asqan
  • 4,319
  • 11
  • 61
  • 100
Niv Kremer
  • 111
  • 1
  • 3
8

You need to give it a container with a height. width uses the viewport as the default width

swider
  • 3,374
  • 2
  • 27
  • 40
3

Without content, the height has no value to calculate the percentage of. The width, however, will take the percentage from the DOM, if no parent is specified. (Using your example) Placing the second div inside the first div, would have rendered a result...example below...

<div id="working">
  <div id="not-working"></div>
</div>

The second div would be 30% of the first div's height.

  • That triggered me to figure it out. I was forgetting that when you build your react element, you have to encase the entire return inside of one single div. So I had my #app div, then a div with no id, then the 3 divs I'm trying to size... I just put an id on the middle div and did my absolute styling on that and bingo! Thanks a ton everyone! :) – Jon Sansoucie May 01 '17 at 17:36