Amazon.com Widgets

Fancy Frames with border-image

image

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.

imageThis 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.

imageNext 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

imageNow 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.

PostInfo

RelatedContent

Previous Post: Safari CSS Wizardry

 

Next Post: New Job

 

Comments

Name:

Email:

URL:

Remember my personal information

Notify me of follow-up comments?

Posted by Ned Schwartz  on  07/31  at  05:20 AM

Hey,

nice write up; I would like to point out that the border image spec (http://dev.w3.org/csswg/css3-background/#the-border-image corroborated by http://developer.apple.com/documentation/AppleApplications/Reference/SafariCSSRef/Articles/StandardCSSProperties.html) states that you can give values for the widths of the border-image slices followed by a slash (/) and 1 - 4 values for the actually border-image enabled widths:

“If the slash is present in the property value, the one to four values after it are used for the width of the border instead of the ‘border-width’ properties (but only if the specified image can be displayed). The order of the values is the same as for ‘border-width’. “

So, you could eliminate the css3 selectors and just write:
.frame{
border:solid #d7d7d7 1px;
/* repeat for -webkit and -moz */
border-image: url(/images/frame.jpg) 1 3 4 3 / 10px 30px 40px stretch;
}

and that SHOULD cover both supporting and non supporting browsers. (I have tested it out myself).

note, I also minimized your css decelerations: declare enough borders like that and your css is going to make YSlow cry.

Posted by Ned Schwartz  on  07/31  at  05:21 AM

(note, I posted that last comment on your follow up first, but thought I should put it here so that if anyone reads the article they can see it!)

Posted by K. Garner  on  08/04  at  09:14 AM

Ned,

Thanks for the tips. I hadn’t read the official spec on border-image so I wasn’t aware of the “slash” trick. That is really useful. And yes, the CSS I included was too verbose. I usually use the condensed declarations, but decided to go the long route to make it more understandable for those that don’t know CSS shortcuts like that. Thanks for reading.

Posted by greetingsfrompoland  on  03/05  at  05:56 AM

Hello to all ! Greetings From Poland. very Good Page !

Posted by slupielpprimb  on  01/02  at  02:19 PM

I highly enjoyed reading this article, keep up writing such exciting articles.

Posted by Ryan  on  01/31  at  11:40 AM

Great material that allows me to make adjustments. I read a little bit of information about this, but it was not nearly the content that you have left. Being able to utilize things others cannot is always in my best interest. Thanks for sharing.

Posted by Tony  on  03/25  at  07:12 AM

These things can be really confusing for people just getting started, but you seem to make things easier than most. As long as people pay attention to what they are reading, then they should be able to pick things like this up or at least apply other tactics. Displaying things like these on blogs helps others because bloggers can comment on things and give their adjustments as well. Nice post and thanks for the information.