
Update 2: I tried out the new Alpha release of Firefox 3.1 and…it works! They will finally support border-image and the nth-of-type selector. Obviously if your requirements dictate identical rendering between IE and Firefox — my condolences — this technique won’t help you out there, but for many (most?) projects I think that the combined marketshare of Firefox and Safari (around 25% by most accounts) gives enough critical mass for adoption of this technique. But YMMV.
Update: with the recent release of Firefox 3 and Opera 9.5 I have written a brief followup on how these versions have affected this technique.
If you haven’t visited this site with Safari 3 yet I suggest you give it a shot. Go ahead, I’ll wait…
Okay, so do you notice anything different? There are actually a few differences, but the easy one to spot is the fancy borders around the images. Techniques have existed for creating this look before, but they are all pretty in-elegant. The usually require you wrap the <img> in a series of <div>s. The best technique I saw used JavaScript to find the image and then inserted the appropriate <div>s programatically. That works well, but one of the goals of this site is to experiment with new techniques and technologies. This means that there are times that I will implement a new technique even if it is only available on a small set of browsers. The trick is to give less advanced browsers an experience that is still good. Notice, I didn’t say identical.
Welcome to CSS 3
It’s important to note that the following techinique currently only works with Safari 3. If you are looking for a technique that will give you this effect on all modern browsers, this ain’t it.
So let’s take a look at how I created this effect. The old way was to apply a set of background-images to the <div>s. My first thought was to use Safari’s support of multiple background images. A very nice technique, but it is not the most elegant option. You still need to create a set of background images and then position them on the <div>. A better option is the newly supported border-image attribute.
This technique only requires one image that is used to extract out the portions that will be used for the border. For this site I am using the image to the right. Let’s take a look at the code. First I give the images that I want to have the frame a class=“frame” — original, I know. Here’s the CSS to create our fancy borders.
.frame {
padding: 4px;
background-color: #fff;
border-top: solid #d7d7d7 1px;
border-right: solid #d7d7d7 3px;
border-bottom: solid #d7d7d7 4px;
border-left: solid #d7d7d7 3px;
-webkit-border-image: url("images/frame.png") 1 3 4 3 stretch stretch;
-moz-border-image: url("images/frame.png") 1 3 4 3 stretch stretch;
border-image: url("images/frame.png") 1 3 4 3 stretch stretch;
}
Let’s look at it more in depth. I want the images to have a white border around them and then have a light gray border (this will get replaced by the images). So I have set the padding and background-color. Next I am establishing the border widths. Since my border is uneven I have different widths on all sides. This is to accommodate the drop shadow in our frame image.
Next is the magic. The next three commands all do the same thing, but since the support of CSS 3 is very browser specific you have to state it a few different ways. The basic parameters are the image you are using for the frame (url(images/frame.png)), then the width of each individual border in the classic top, right, bottom, left order so familiar to us. What this actually does is divide the image into 8 pieces. Four images for the corners and four for the edges.
The next item is the stretch parameter. This tells the browser how to fit the border-image to the object. You can stretch it to fit or round it to fill the area as best as possible by repeating. This only affects the images that will be used for the edges. Once you do that you now have an image with our cool frame. There is only one small problem.
If you have your image on a white background you won’t see it, but there is one small problem. The background-color is actually filled in behind the border. Since our border features a semi-transparent drop shadow that tapers you will still see this white background peeking out from behind the border if your image is on a colored background. Fortunately, there is a simple work-around. Just add -webkit-background-clip: padding; to your CSS and your done. This tells the background to stop at the edge of the padding and not enter the area of the border.
Accommodations
Now we have an image with a nice frame and everything is right with the world. Well, except when you visit from a browser other than Safari 3 (but I still hope that the final release of Firefox 3 will add support as well). To other browsers we now see the considerably less nice looking image you see to the right. This is due to the uneven borders we gave the image to accommodate our drop shadows. What we need to do is have some way to target only browsers that understand CSS 3 and feed them the fancy frame. All others get the following code.
.frame {
padding: 4px;
background-color: #fff;
border: solid #d7d7d7 1px;
}
Enter CSS 3 Selectors
CSS 3 selectors offer us ways of targeting elements with even more discrimination than simple ids and classes. This particular technique uses the :nth-of-type() selector. Simply adding .frame:nth-of-type(1n) to our fancy frame definition allows Safari to target every image with a class=“frame”. So now the should CSS should look like this.
/* for non-CSS 3 aware browsers */
.frame {
padding: 4px;
background-color: #fff;
border: solid #d7d7d7 1px;
}
/* for CSS 3 aware browsers */
.frame:nth-of-type(1n) {
padding: 4px;
background-color: #fff;
border-top: solid #d7d7d7 1px;
border-right: solid #d7d7d7 3px;
border-bottom: solid #d7d7d7 4px;
border-left: solid #d7d7d7 3px;
-webkit-border-image: url("images/frame.png") 1 3 4 3 stretch stretch;
-moz-border-image: url("images/frame.png") 1 3 4 3 stretch stretch;
border-image: url("images/frame.png") 1 3 4 3 stretch stretch;
-webkit-background-clip: padding;
}
Now, those who visit with less advanced browsers still get a nice frame around the images. And those who visit with more advanced browsers get something of an extra treat. Remember, if having this look on modern (but not CSS 3 aware) browsers is critical for your site, then this technique won’t help, but if you want to give your visitors that are using CSS 3 browsers a little something extra, this technique will work well.