Eight Corners Under One Roof

Making a dynamic border using only one image

One of the questions you often hear is "Why does my site load so slow - it's only a hundred K yet takes two minutes." You open up the page to have a look, and sit there watching the hundred or so 100 byte images try to load. Separate images even though they are the same STYLE just because the width is different, separate images for each corner, separate images just because a corner is different from the previous corner - and none of it is necessary.

First we should explain why combining multiple images down to a single file is desirable, as well as cover when it is NOT desirable. Every time a file is requested from a server, you first have to send the request which takes roughly the same amount of time as a 'ping' - then you have to get the 'acknowledge' - THEN the data starts to arrive as separate packets. This request and acknowledge is called 'handshaking' and in an ideal world would only take about 20ms. We do not live in an ideal world.

Ping times real world tend to run between 100 and 300ms on average, and for dialup and cable users numbers over 700ms are not out of the ordinary. You multiply this twice for the signal each direction, figure in waiting in line if the server is particularly busy, times eight files (one for each corner on a box) and suddenly you've jumped from a page that in terms of size should take ten seconds on dialup suddenly taking that long on broadband.

On the server side, you also need to keep in mind that if the page isn't getting proper cache hits, extra files means extra filesystem seeks and reads, meaning increased IOWAIT. A single file that's not fragmented WILL read a lot faster than multiple small files.

So it's desirable to minimize the number of images to as few as possible... BUT one needs to keep in mind that 'real world' a 200ms handshake is about the norm. If the combined file would be BIGGER than it would be as separate files AND that amount larger would take longer to transfer than the handshaking, then you've wasted your time. You also have to keep in mind that it is often more code to use just one image, especially in the HTML, so you have to weight that as well.

Leaving us with the question what images on a page are best suited to being combined down? The most obvious answer are borders. They usually are in the same 'color range' meaning that they can be palettized together. When encoding pallete reduced images a copy of the palette, usually 768 bytes worth (or less for 16 color gif) is included in each file - if the image itself can be encoded to less than the size of the palette, your combined file can often be smaller than they are as separate images! Borders usually have four sides that are tiled - tiled images means long repeats of the same color on one axis, meaning the encoder can make the image even smaller.

So now that we know what we should target, enough prattling let's do it. First we need to make an image. For the discussion here I'll make a simple grey shaded border.

borders

In the center we have the top and bottom borders - in this case 640px wide total. We can use a technique called 'sliding doors' to create the top and bottom 'stripes' using a minimum of html and CSS. The only 'problem' with said technique is that the maximum width our container can grow to is that 640px - if you want wider you have to make a wider image. I used 640px in this case so you can see it. Normally I'd make one 3072px wide - which BTW wouldn't really increase the size of the file all that much because we're using .png, and png does a REALLY good job encoding. Case in point, have a look at borders_biggie.png which is only 300 bytes larger a file than the 640px version, despite being almost six times wider. You can use that image instead of the one here by simply replacing all occurances of 'borders.png' with 'borders_biggie.png' and changing all occurances of -640px to -3072px.

On the sides of our images we have the parts that will be tiled vertically. By placing them on the left and right side, we can just use background-position left and right to put them on our wrapper - then use numeric positioning on all the other elements.

So, let's see some code.

	<div class="wideBox">
		<div class="borderTop"><b></b></div>
		<div class="borderLeft"><div class="borderRight"><div class="borderMiddle">
			<p>Some test content</p>
		</div></div></div>
		<div class="borderBottom"><b></b></div>
	</div>
	

The HTML is pretty basic - a master container by which we can set the total width and nab all sub-classes should we need to 'isolate' them. (we'll use that in our last example), .borderTop and .borderBottom are pretty much the same construct for sliding doors, so in the CSS we can share a lot of the properties (reducing the total code). You'll notice I'm using the bold tag instead of a div - since in sliding doors the inner container is position:absolute, and therin by definition also display:block, we can use just about any tag there. I'm using <B> because it's only one character wide and with no actual content semantics mean exactly two things here, and Jack left town.

The middle section is a 'triple nest' to which we'll apply padding-left to .borderLeft, padding-right to .borderRight, and a background-color to the center area to hide the 'overflow' images.

On to the CSS. First, let's handle the .borderBottom and .borderTop classes

.borderTop,
.borderBottom {
	height:16px;
	background:url(images/borders.png) -16px 0;
	position:relative;
	font-size:1px;
}

.borderBottom {
	background-position:-16px -16px;
}
	

As you can see the two share much the same properties since they are both 16px tall, use font-size:1px to work around IE not liking elements to be shorter than font-size and line-height, both need to be position:relative so we can absolute position the bold tag 'blocks' inside them and even share the same background image. Only the background-position between them is different. The top one of course gets a background-position y of 0 to use the top half of our 32px tall image, while the latter one has the image 'slid up' 16px to show the bottom half. Both are slid 16 pixels left so that the left side tile doesn't show up. Because the two of these auto-expand to the width of the container we don't need to play with the widths at all. This gives us our entire top image except for the top right corner.

The bold tags inside those get the following CSS:

.borderTop b,
.borderBottom b {
	position:absolute;
	top:0;
	right:0;
	width:16px;
	height:16px;
	background:url(images/borders.png) -640px 0;
}

.borderBottom b {
	background-position:-640px -16px;
}

	

Again the two of these are the same except for their background-positions. They are set to position:absolute in the top right corner - because the div around them is set to position relative, this puts it in the top right corner of that box, NOT of the screen. Setting them to 16px by 16px means it will only show that small tile of the background meaning that when we slide the background left 640px we end up only showing that top right corner. Simple.

The 'middle' part isn't much more complex.

.borderLeft {
	padding-left:16px;
	background:url(images/borders.png) top left repeat-y;
}

.borderRight {
	background:url(images/borders.png) top right repeat-y;
	padding-right:16px;
}

.borderMiddle {
	background:#DDD;
}
	

On .borderLeft we just pad the left side, and tile our image top left... because the left side is padded .borderRight will not overwrite the part of the image we WANT to show. .borderRight then does the same thing just to the opposite side. At this point the entire image would appear tiled in our middle area, so by assigning it a background color (or you could use a second image if desired) we overwrite the parts we don't want to show.

Here are a couple examples - the first box is has it's outer wrapping .wideBox set to 640px wide, the second one called .narrowBox is set to 256px wide.

Some test content

Second one to show the box 'smaller' and taller

with a second paragraph too. Ten thousand lives for the Coordinator!

Taking it further - using the same code for dropshadows

The border we're using in this example can also be used as a dropshadow with no modificiations to the HTML and only a minor change to the CSS. The only thing we change is .borderMiddle - and to reuse the same CSS we'll target just the .borderMiddle inside a new outer div class .dropShadowBox replacing .narrowBox in our code.

.dropShadowBox .borderMiddle {
	margin:-16px 0 0 -16px;
	position:relative;
	border:1px solid #888;
}
	

The negative margins top and left make our .borderMiddle expand up over the top and left borders, the position:relative makes sure that .borderMiddle is depth sorted over said borders, and the border just makes it look pretty.

Which looks like this:

By sliding the middle box up and left using margins, we can turn our border into a drop shadow without changing the HTML!

Even further

A lot of people will balk at the extra code needed to wrap these boxes, and in a number of cases rightly so. This could be alleviated by adding the inner DIV's with javascript while having the outer CSS provide a 'normal' border. Additionally you could keep adding other top and bottom border styles in the vertical to the image if desired (though to use different sides you end up having to resort to using more images). This is handy if you are also making rounded or styled inner corners for things like headers. Examples of that can be seen on my javascripted border demo page . The javascript method does have some issues though as using onload often makes some browsers like Opera and Firefox render it incorrectly on first pageload, though a refresh corrects the problem. (strange, no?)

Conclusion:

As you can see this is a very powerful technique which used properly can save bandwidth, reduce pageload times, all for relatively little html. It does take a bit of understanding how the box model and depth sorting works, and is not a cure-all for every situation - but with practice this technique is usually well worth the effort... Especially since not only does it work in all modern browsers, it works all the way back to IE 5.5 and 5.2 with no hacks, and validates in everything from 4.0 Tranny right up to XHTML 1.0 Strict... and if nothing else is a HELL of a lot less code than trying to wrap all this in a table with separate files.

Contents of this page © Jason M. Knight, 2007. Feel free to use the code presented here, but if you are going to quote this tutorial please at least link and give credit as due. Released to the public domain as there's none of that open source bullshit allowed in here. Lands sake if you are going to give something away, just ******* give it away! Remember, when people try to sell you on something as 'freedom', then load it down with a bunch of restrictions... well, does the term 'snake oil' ring a bell?