One of my first blog posts (way back in April 2003 and on the old DotNet Weblogs site, now Weblogs @ ASP.Net) was called Pet Peeve – Using HTML Tables to Control Web Page Layout. Since that early post I really haven’t seen much headway made in the .Net camp to oust the Table camp and replace them with the CSS Camp. There are some things that are still pretty hard to do using pure Divs and CSS, which hinders the designing without tables movement (that and all major HTML Editor pretty much force Table layout down your throat). Sure, VS 2005 is a big improvement for the CSS camp, and Microsoft New Web Designer (code named Quartz) looks to be heading in the right direction, but until we create better tutorials on how to use CSS instead of Tables, most folks will not use CSS for layout.
The one thing that I always dropped back into table layout mode was when I wanted to create custom borders that could automatically resize. That was until just recently. Over the long holiday weekend I took the time to sit down and try to come up with a pure Div and CSS template that I could use everywhere, on any browser, and with no javascript (like CSS layouts are supposed to be). The site 456 Berea St. has a good article on Custom borders with advanced CSS, but it uses features currently implemented only in Safari. For a cross-browser approach, Søren Madsen’s CSS Design: Creating Custom Corners & Borders was the approach that most CSS’ers used, since it had a more “semantically logical” feel to the markup. The problem I always had with Søren’s approach has to do with graphics he uses. They have an inherent scale limit, since he is clipping the image instead of repeating the border images in the x or y direction. But, he has to do that to keep with the semantically logical markup. In an environment that has both vector and layout markup languages in the rendering engine, Søren’s approach would be great (can you say Windows Presentation Framework?), but I wanted something that a Table layout person could easily wrap their head around without resorting to using tables (Mozilla has a implementation that just replaces the Table, TR, and TD elements with Divs, and makes use of special CSS pseudo table classes, but that markup is god awful. If you must see it yourself, here’s a link to an example on my site, but remember it only renders correctly in Mozilla).
So, what I did is break down how I would normally use tables to create scaling custom borders, and then build it back up using XHTML and CSS. I hit a bunch of gotchas along the way, but I think I finally came up with something the meets my personal requirements of not using tables, that worked on all modern browsers, but also living within the boundaries of XHTML and CSS.
When you create a graphic in something like Adobe Illustrator or Photoshop, to get a border to scale, you usually slice up the graphic and place the parts in what I call the 9 Box Layout:

Using table layout, you would create a table with 3 rows and 3 columns, with each cell mapping to its counterpart in the 9 Box layout (Table Layout Example). Boxes 1,3,7,9 would not be repeating images. Box 5 would have no image, only a solid background color. Boxes 2 and 8 would normally be a small slice of that part of the image (only a couple pixels wide), and would repeat in the x direction. Boxes 4 & 6, would be similar to 2 & 8, except that the image would only be a couple pixels in height, and repeated in the y direction. Its simplicity is the reason why the Table layout for custom borders is so popular. Once you have this done, all you need to do is set the width of the table, and the horizontal border scale without any other changes. To adjust the height, just add you content to Box 5, and the height scales to the size of the content.
Now, I want to do the same exact thing, but with Divs and CSS, without using CSS pseudo classes that are not supported in all browsers and still have markup that isn’t a total mess. Remember, if you want to force IE 6 into standards- compliant mode, you need to use the !DOCTYPE “switch” and put the proper DTD at the top of the document. Otherwise IE renders differently than what is expected. We will start with div to set the container for the “table”, set the width, and place in it what will be the Div for top row (Boxes 1,2,3).
<div id="Panel" class="TableLikeContainer" style="width:200px;">
<div id="Box2">
</div>
</div>
CSS:
body {
background-color:#CCCCFF
}
.TableLikeContainer {
padding:0;
margin:0;
}
Since we are using CSS for layout we can use the CSS Float attribute to float Box 1 to the left corner and Box 3 into the right corner. We then set the background image for Box 2 to the same one in the Table Layout version and repeat that in the x direction. Now we have Box 2 with the corners (Boxes 1 & 3) floating over the top of the row’s background image, which gives us scaling in the x direction. If you want to have some text in the header, you can put that after the floating corner divs.
<div id="Box2" class="CustomBorderTopBorder">
<div id="Box1" class="CustomBorderTopLeftBorder"> </div>
<div id="Box3" class="CustomBorderTopRightBorder"> </div>
<!--<h2>top</h2>-->
</div>
CSS:
.CustomBorderTopBorder {
height:30px;
background: url(images/TopCenterBorder.jpg) repeat-x;
}
.CustomBorderTopBorder h2 {
font-size:medium;
text-align:center;
padding-top:10px;
margin-top:0;
margin-bottom:0;
}
.CustomBorderTopLeftBorder {
width:30px;
height:30px;
float:left;
background: url(images/TopLeftBorder.jpg) no-repeat;
}
.CustomBorderTopRightBorder {
width:30px;
height:30px;
float:right;
background: url(images/TopRightBorder.jpg) no-repeat;
}
What I found is that Mozilla doesn’t like to render the background images in Boxes 1 & 3 when they are empty, so I had to place non-breaking spaces in them to get Mozilla to render them. You can repeat this for the bottom row, just swapping out the top images to the equivalent bottom ones.
So, I got the top and bottom done pretty easily using the CSS float attribute. You may think that you could do the same thing with the middle row, but due to the nature of floating, they do not automatically scale to the height of the container (which makes sense for floating, but it isn’t what we want here). So, I had to get a little more complicated then I would have liked, and had to nest the Divs, so that Box 4 contains Box 6, which then contains Box 5. Sort of funky smelling, but it works.
<div id="Box4" class="CustomBorderLeftBorder">
<div id="Box6" class="CustomBorderRightBorder">
<div id="Box5" class="CustomBorderCenterContent">
</div>
</div>
</div>
CSS:
.CustomBorderLeftBorder {
height:100%;
background: url(images/MiddleLeftBorder.jpg) repeat-y;
}
.CustomBorderRightBorder {
background: url(images/MiddleRightBorder.jpg) repeat-y 100% 0;
}
.CustomBorderCenterContent {
margin-left:30px;
margin-right:30px;
background-color:white;
}
The trick here is to nest them, so that the outer boxes (4 & 6) have to scale with the content, and repeat the image along either side along the y axis. Another trick is to set the left and right margin of Box 5 to the width of the border graphic, which will make sure your content is indented on the left side, and leave the proper amount of space on the right, so you can just set the position of the repeating right border to 100% (since the width of the border is not part of the overall width of Box 5, see the definition of the Box model if you want the gory details of why this works and is actually correct). Using the Box model to our advantage helps us not have to hard code the width of the “table” anywhere in the markup or the CSS. The width is set by the CSS width attribute on the “table” Div (which I labeled Panel). Now, any content you have can be placed within the Box 5, and everything will scale.
Well, that last statement isn’t 100% correct. Everything almost always scales in all browsers. Turns out that the feature we used to our advantage in laying out the right border for Box 6, aka the Box Model, comes back and bites us, but only for non IE 6 browsers. If you only use Divs within Box 5, everything looks great in Mozilla, but who uses all Divs? If you use P, Hn, or OL elements, they will automatically set the top and bottom margins (which will give a paragraph some space between it and other markup). Remember I said that in the Box Model Margins are not used to calculate the width of the element, well the same is true for the height. So the height of the nested boxes (4,5,6) does not match the distance from the Top and Bottom Rows which creates some extra space between the middle row and the other rows. But, for some reason, although IE is supposed to be in standards-compliant mode, it still uses the wrong height and width calculation for of the background size and location. It may look “right”, but I tend to trust Mozilla’s implementation of the Box Model over IE (but I haven’t verified which one is actually correct). The solution is pretty easy, just set the border-top and border-bottom to 0, and instead use padding-top and padding-bottom (which, according to the Box model is included in the height and width calculation).
<div id="Box5" class="CustomBorderCenterContent">
<!-- margins don't count in the height -->
<p>asdfadsf</p>
<p>asdfadsf</p>
</div>
CSS:
.CustomBorderCenterContent p {
margin:0;
padding-top:0.25em;
padding-bottom:0.25em;
}
The Box Model will also mess with the content in both the top and bottom rows, so you have to remember to do a similar thing there too.
.CustomBorderTopBorder h2 {
font-size:medium;
text-align:center;
padding-top:10px;
margin-top:0;
margin-bottom:0;
}
Hopefully, this is something that the Table Layout crowd can use to wean themselves off of tables and over to CSS driven layouts. One of these days I’m actually going to migrate this site off of the old .Text blog engine and onto CommunityServer 2.0, and while reskinning the entire site, use these templates. Although Scott seems to have drunk the CSS cool-aid, there are still lots of tables used for layout in the default skins.
If you want to download these examples and play with them yourself, I zip them up for you (and even included the Illustrator file I used to create the custom borders).