The "box model" in CSS works like this:
width + padding + border = actual visible/rendered width of box
height + padding + border = actual visible/rendered height of box
It’s a little weird, but you get used to it. In IE 6 and earlier, when in "quirks mode," the box model was treated differently.
width = actual visible/rendered width of box
height = actual visible/rendered height of box
The border and padding values moved inward, cutting into the width/height of the box rather than expanding it. Here is a dramatic example of the difference:
It was weird quirk to have to deal with, although the fix was usually as easy as setting a proper DOCTYPE and getting out of quirks mode. More to the point, some people prefered this handling of the box model, claiming it to be more intuitive. It’s a valid point. Having the actual visible width of a box not be what you declared in the CSS is a bit mind bending.
The mind bending continues with the modern implementation. Let’s say you have a block-level element like a <div>
on the page. It’s within another parent <div>
with a width of 500px. The child div will automatically default to 100% as wide as the parent (500px) without any special CSS. That’s how block-level elements work. But now let’s say you add 10px of padding all around and a 2px border. The child div will behave like the old quirks-mode box and the padding and border will cut into the box. The visible box will remain 500px in width. Now if you go and explicitly declare the width of that child div to be 100%, the div will actually grow and break out of the parent. The width will start at 500px, but then 10px of padding and 2px of border on either side will leave the visible box at 524px.
If you wanted to ensure the box kissed the edges of the parent, but still use a 100% width declaration, you’d have to set the child divs width to 476px. But now we’ve run into a problem, and that’s fluid width design. Let’s say instead of the parent being 500px, you use 72%. Now what do you set the inner divs width to be to make sure it kisses the edges? There is no answer with the current box model. The percentages mixed with pixel values just don’t mix well. Setting the inner divs width to 95.2% will work when the parent just happens to be exactly 500px, but if it grows wider, as happens in fluid width design, the div will be too narrow, and if the parent grows narrower, the inner div will be too wide and break out of the parent.
This <div>
pseudo markup only takes us so far, because the answer of course is to not use a 100% width declaration. Removing that will solve our problems. However, there is one very important element which really needs this 100% width declaration, and that is the venerable <textarea>
.
The <textarea>
doesn’t care if you make it a block level element, it’s width will be determined by the required cols
attribute unless you explicitly set it. In a fluid width environment, it is very likely you’ll want a textarea that is fluid width as well and your layout may demand that it matches up nicely both left and right. In fact, in the current design of this site, that is the case.
Notice the textarea lines up with the text inputs at either width. The left edge and right edge are also of equal distance from other objects.
To accomplish this, we want to be using 100% width and pixel-based padding and border, and that is the unholy combination that is seemingly impossible. Even the great designer Jon Hicks, known for his excellent fluid width designs, had this to say on the subject in the CSS Wishlist I put together a few years back:
I would love a different box model! I find it bizarre that padding and border add the width of an object, and would love to be able to give something like a textarea 100% width and 3px padding without worrying what it’s going to do the layout. Perhaps something like padding-inside as a new selector?
In that vein I also wish I could specify a 100% width for an element, minus a set fixed width. Again, very useful when creating fluid designs with form elements!
Kapow!
In an epic The DaVinci Code-like plot twist, the answer to our problem is right where we started, the box model itself. If we could force our <textarea>
to have padding and border that cut into it’s 100% width instead of expanding it, we’d be all set. Wouldn’t you know it? There is a way!
textarea {
-webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
-moz-box-sizing: border-box; /* Firefox, other Gecko */
box-sizing: border-box; /* Opera/IE 8+ */
}
The box-sizing
CSS3 property can do just this. The border-box
value (as opposed to the content-box
default) makes the final rendered box the declared width, and any border and padding cut inside the box. We can now safely declare our textarea to be of 100% width, including pixel-based padding and border, and accomplish out layout perfectly.
Support
Chrome (any): box-sizing
Opera 8.5+: box-sizing
Firefox (any): -moz-box-sizing
Safari 3: -webkit-box-sizing (unprefixed in 5.1+ versions)
IE8+: box-sizing
Min / Max
Box-sizing should and does work with min-width
/ max-width
/ min-height
/max-height
with one exception: Firefox. In even modern Firefox, v11 at the time of this update, min-height
and max-height
don’t respect box-sizing. See this fiddle by Beau Smith to illustrate the problem. This is a known bug since 2005.