The reason for this article is that I’m doing a completely table-less layout for a client that needs every frigging WH SEO trick in the book (paranoid mortgage guy). There are several elements of the design that require a pliable rounded-rectangle box surrounding a changing block of text. So I started Googling and found lots of people that have done it, but in every case there was a trade off … it won’t work in this browser, it’s too complicated… only stretches horizontally or vertically, requires too much knowledge of the final size of the box, it didn’t work period… it needed to be written specifically for the content inside or it didn’t size right… you name it, always a problem.
So while talking to Nutballs about an unrelated challenge I asked him if he had any experience with such stuff and he had a bit, but had not done what I wanted – which was a 4-corner asymmetrical box in all CSS - the four corners would NOT be a simple rounding, they could conceivably be completely different. (Side note - my next site update for a client has a linen-rag looking backdrop for forms that must behave the same, but is anything but rounded corners). But he said one thing, (which I will demonstrate in a moment) that broke the whole thing wide open and helped me find a solution exactly the way I needed it. He said
he'd seen CSS images overlap and stretch horizontally in all the browsers. That little fact was all I needed.
The common thread among all of the solutions that I found was that they tried to put all of the CSS into a single box – in other words, they tried to imagine a single rectangular DIV that managed to get a background image to show it’s 4 corners in the right position, then make it stretchy enough so that it would almost work right. (There was one solution from an Italian guy who called it "liquid corners" who got closer to the way that I did it here but still did not pull it off right).
(This is the problem with so called webmasters and CSS gurus – they see something that they want to get done but cannot think like a programmer, so the solutions involve the hackiest kludgy solutions around, rather than pulling back and reducing complexity, to get a stable, robust and reliable solution to a problem. I wish I had a nickel for every time I read "the math was really complex" or "this hack is required" or "this will only work when..." Grrrrr.)But I digress. Most of you, probably just like me, if you've wanted a rounded-corner box, would create a 9-cell table: a TL corner, stretchy top, TR corner, stretchy left, body area, stretchy right, BL corner, stretchy bottom, and a BR corner. This works because we put stretchiness where we need it and lock in fixed graphics (the corners). As it happens, the CSS solution is pretty durn close.
A complete self-contained demo of this method can be found here:
http://www.perkiset.org/demos/cssboxdemo.html
Stretch box around to see the CSS in action. View Source to see the whole thingFunctional TheoryThere are lots of examples of a vertical stretchy box in CSS, and examples of a horizontal stretchy box. It’s the combination that makes things rough. So let’s put stretchiness only where we need it. Consider the box as 3 rows. All of these rows are horizontally stretchy, but the top and bottom are of a fixed height. The middle row is the only one that is really stretchy, but all that graphically needs to stretch is a “left bar” graphic and a “right bar” graphic – we don’t care about the corners because that’s taken care of by the top row and the bottom row.
The trick takes 3 images: a 4-corner image and a left and right bar image. The three I built for my client can be seen here:



All of them are really small PNGs, so download time is not an issue.
Here is the required CSS. I’ll outline what’s going on in a moment:
.plaqueTopLeft, .plaqueTopRight, .plaqueBottomLeft, .plaqueBottomRight { background-image: url("css3DBox.png"); }
.plaqueTopLeft { height: 16px; background-position: 0 0; }
.plaqueTopRight { height: 16px; background-position: 100% 0; margin-left: 20px; }
.plaqueBottomLeft { height: 28px; background-position: 0 100%; }
.plaqueBottomRight { height: 28px; background-position: 100% 100%; margin-left: 20px; }
.plaqueLeft { background-image: url("css3DLeft.png"); background-repeat: repeat-y; }
.plaqueRight { background-image: url("css3DRight.png"); background-position: 100% 0; background-repeat: repeat-y; margin-left: 20px; }
.plaqueCenter { margin-right: 30px; margin-left: 0px; }
Now here is an actual box on a web page:
<div class="plaqueTopLeft"><div class="plaqueTopRight"></div></div> <!-- the top row -->
<div class="plaqueLeft"><div class="plaqueRight"><div class="plaqueCenter">
This is where all the content goes … it’s a normal div, you can put anything you want in here.
</div></div></div>
<div class="plaqueBottomLeft"><div class="plaqueBottomRight"> </div></div> <!-- the bottom row -->
How it worksLike many good HTML illusions, this only looks like one cohesive box. It's actually 3 rows of embedded divs, all constrained by an outer div to make them work together. (The outer div is implied here - it could be the page itself, or something like <div style="width: 50%"> it doesn't matter)
Let's take just the first row and the CSS that affects it and tear it apart (I've trimmed out stuff that doesn't apply):
.plaqueTopLeft, .plaqueTopRight { background-image: url("css3DBox.png"); }
.plaqueTopLeft { height: 16px; background-position: 0 0; }
.plaqueTopRight { height: 16px; background-position: 100% 0; margin-left: 20px; }
And the on page html...
<div class="plaqueTopLeft"><div class="plaqueTopRight"> </div></div>
Basically I have a div (plaqueTopLeft) that has a background image of css3DBox, and the background image should be placed at the 0, 0 position in the div. Note that I've only put the positioning here for clarity - this is the default and not required. Then inside of that div, I place another one (plaqueTopRight) with the same background image (this keeps download time really fast) however I tell it that I want the background image placed 100% right and 0 top. I also tell the plaqueTopRight div that I want it to have a margin of 20 px from the left of it's containing div... in this case, the the plaqueTopLeft div - so I get to see the rounded corner of the LEFT div and the rounded right corner of the RIGHT div completes the right side. If you try just that much HTML for yourself (scoff the images right from here) you'll see how the div is now horizontally stretchy. You'll also notice that if you create boxes larger than 800px this will fail because the images I made are only 800 px - so if you stretch too far apart you'll get white space. Fix this by simply making an image that's 2000 px across or something. Again, you'll only need to download it to the client once so it'll be pretty quick.
The second row uses the css descriptors for plaqueLeft and plaqueRight - which contain different images but do the exact same thing. Here's where the cool part comes in though - by repeating the Y of the background image, now the middle portion becomes VERTICALLY stretchy and baddabing, my div can now stretch horizontally or vertically and the graphics we have so far will adapt perfectly. Note that I also have plaqueCenter in the middle of the plaqueLeft and plaqueRight divs - this is to "reign in" my text on the right side.
The last row uses the css descriptors for the plaqueBottomLeft and plaqueBottomRight - again they do virtually the same thing as the plaqueTopLeft and plaqueTopRight except that this time we set the background position to be at the bottom.
Customizing for your own graphicsThere are 4 magic numbers in the CSS above: 16px, 20px 28px and 30px. They all apply to this specific graphic. the tops are 16px tall because that makes the DIVs big enough to display the entire round corner. The bottoms are 28px high, because they must display the rounded corner AND the drop shadow. (Natch the asymmetry). The 20px margin is a somewhat arbitrary number, but it keeps the plaqueTopRight and BottomRight divs from "encroaching" on the left side divs. The margin 30px is what I need in the center div to keep my content out of the right-side 3d effect and drop shadow. So after having created your own graphic containers, you'll want to tweak these numbers so that you get the amount of padding and graphical effect your looking for.
Net-netRather than a complicated, hack-ridden and persnickety method for an asymmetrical rectangular container, this is a stable, standards compliant method that avoids the quirks that normally plague IE/FF/Safari browsers by way of interpretation irregularities. The only customization required for each box is to tailor the numbers to fit the graphical image, but since it's a custom image, that much is expected. Other than that, the CSS is replicable and easily reusable for any number of situations.
I tested the method in Safari, FF Mac, IE 6, IE 7 and FF for windows, it all works great. I don’t have IE 5, so if someone really wants to give that a shot that’d be great, but frankly, I’m rather unconcerned about that.
Enjoy!
/perk