A few days ago I was searching for the best production usable solution for rounded corners in XHTML/CSS. I know there is no perfect solution, as many people have different needs and each new technique has its disadvantages. But I finally found something very close to it …
What I was looking for:
- usable in a production environment: should work in all major browsers
- should satisfy designers: should make it possible to have fancy borders, gradient backgrounds, drop shadows, etc
- should satisfy coders: usable, accessible, semantic (to a certain extent), no JavaScript, not too many unnecessary extra elements or hacks
- flexible: resizable, shrink-wrappable, same code for big boxes or small link buttons, easy to maintain, easy to adjust
- optional: creating them should not involve too much work
Nearly every technique I used before needed a fixed width or had only two rounded corners or had other drawbacks. I had only found one single technique that fulfilled all my basic needs, but was not very handy to use and had other limitations (such as no way to do a decent hover effect, difficult to maintain and one annoying bug in IE 6).
While I was searching for a new technique I came across one which already came quite close: Creating bulletproof graphic link buttons with CSS
But this technique has a few drawbacks:
- Additional to one big background image with all of the necessary elements in it, it also uses a second one just for the corners.
- It cannot handle fancy backgrounds.
- It doesn’t allow nesting of elements with rounded corners.
- Using the same technique for other elements needs adjusting.
- No fancy hover effects are possible.
So, I fiddled around with it and improved and enhanced it. Here is how:
0. The Original
I won’t repeat what Roger Johansson wrote in his original article. I recommend reading it first to understand what I’m talking about.
1. One Background Image
The first thing I wanted to get rid of is this strange second background image. Although the corner.gif
image contains all of the four corners with a lot of transparent space in between them, the original technique uses it only for the top right and bottom right corner. So, at least 90% of that image is not really needed.
I soon noticed that the bottom right corner does not need to use corner.gif
, but can use the normal button.gif
just like the rest. So, the only thing to fix was the top right corner.
Every other corner is easy to position because each previous element has a padding exactly where the following element cannot overlap the previous corner. The only tricky bit is the first corner that isn’t “protected” by a previous padding: the top left one. To “protect” the top left corner nonetheless, I just gave the first span a left margin, which I removed again from the second span with a negative margin of the same value. Et voilà, no second image needed.
Step 1: One Background Image (Demo)
2. One Span Less
Then I wondered: Why are there five background image placements and not four? The last innermost span has the background strangely attached to “right center”. And sure enough, I could safely delete the last span and thus could get rid of one unneeded span (13 characters per rounded element after all).
The padding from that deleted span is plainly added to the preceding span.
3. Fancier Background
Why should it be “tricky to give the button a non-flat background colour”? Mainly because the last, innermost span defines what the major part of the button looks like. Only because the original version chose that to be from the bottom right (and therefore without the 6px on the top and on the left) it is difficult to use an image with a gradient higher than 6px. It could use a much bigger gradient on the bottom without any problems, though.
But as gradients are mostly found at the top, I simply turned the order around from “top left > top right > bottom left > bottom right” to “bottom left > bottom right > top left > top right”. And now you can have much higher gradients (as high as the container could get minus the 6px at the bottom).
Step 3: Fancier Background (Demo)
4. Rearrangement
Then I rearranged the code a bit, basically to removed things unrelated to the link nature from .button:link
, etc and to move every occurrence of the background image to one single line, so that it’s easier to exchange.
I also fixed a bug in IE 6 introduced with the fancier background changes: IE 6 doesn’t like a bottom padding on the now innermost span.
5. Hover
With those changes it is much easier to exchange the background image on hover with just a few more lines of code.
6. Hoverissimo
Some time ago my boss wished a link button that exchanges the gradient on hover from top to bottom. With this technique the only problem was the aforementioned problem that you can either have a link button with its main background at the top or at the bottom. To be able to exchange these background gradient positions, I had to turn the order of the background position around again (as in Step 3) as they were before, but this time only for the hover state.
7. Other Button Types
Now adding other different button types is a piece of cake: Simply add another class for each new type and give it a different background image (and change the background and font colour accordingly, if necessary).
Step 7: Other Button Types (Demo)
8.More Universal
Now there are only two problems left: If you plan to use this code not only for link buttons, but also for bigger boxes with block elements, you cannot use the code as it is now. You could just exchange the spans with divs and duplicate each line in the CSS code to accommodate that. But what if you have to use another div in there?
Although I’ve normally long outgrown the ‘classitis’ phase, in this case I think, using classes makes the code much more flexible to use. It reduces the CSS code, but increases the HTML again (by 42 characters per rounded element).
And I normally like to give semantic class names, but here rounded corners itself have no semantic meaning, so I contended myself with giving the classes names that are short and have a functional meaning instead. I also renamed “button” to “rounded”, because as soon as this technique isn’t used on a link button, its name would be erroneous.
9. Any Container
Finally, with these former changes it is now possible to use this technique on any container, not only on links or inline elements, but on bigger boxes, too. And these rounded containers can even contain other rounded containers.
Final Code
My final basic code (without the “hoverissimo” code for the sake of brevity):
<a class="rounded <type>" href="#"> <span class="i1 bg"><span class="i2 bg"><span class="i3 bg"> Link </span></span></span> </a> <div class="rounded <type>"> <div class="i1 bg"><div class="i2 bg"><div class="i3 bg"> <p>Block level content</p> </div></div></div> </div>
.rounded, .rounded .bg { background-repeat: no-repeat; display: block; } .rounded { float:left; background-position:left bottom; } .rounded .i1 { padding:0 0 9px; background-position:right bottom; margin-left: 6px; } .rounded .i2 { padding:0 0 0 6px; background-position:left top; margin-left: -6px; } .rounded .i3 { padding:9px 12px 0 6px /* no bottom padding because of IE6 */; background-position:right top; }
/* for each type */ .<type>, .<type> .bg { background-image: url(<type_image>.gif); background-color: <type_background_colour>; color: <type_font_colour>; } /* for hover effect on links */ a.<type>:hover, a.<type>:hover .bg { background-image: url(<type_hover_image>.gif); } /* left out :link, :visited, :active and :focus state for documenting purposes and readability */
I called this technique “Sliding Doors Squared”, because it is basically an enhanced version of the Sliding Doors technique. Although this technique is great and recommended for two rounded corners with a flexible width or four rounded corners with a fixed width, it cannot be used for completely flexible rounded corners. The “Sliding Doors Squared” technique uses the Sliding Doors technique not only horizontally, but also vertically and therefore fills this gap.
Despite all its advantages, there are some disadvantages:
- the background image has to be rather large (the maximum width and height of the container are defined by the width and height of the background image)
- CSS sprites cannot be used for hover effects (at least not before CSS3), so there will be two requests and a flicker at first
- the corners must not be transparent
As yet I have tested this technique in IE 6, IE 7, Firefox 3, Opera 9 and Chrome and it behaves very well so far without any differences or even hacks (no, not even for IE 6!).
For just two rounded corners or simple four rounded corners with a fixed width this might be overkill. But as soon as you need more flexibility or need an easy way to use drop shadows, fancy borders, gradients or link states, this is the ultimate technique to use.
PS: After I finished the code I took a look at the comments for the original article and realised that the first two steps were already solved in there. That could have saved me about 30 minutes, but at least playing with the code got me going further.
A colleague of mine found a simple but effective way of implementing *real* buttons (submit input fields):
Just put the button *inside* the surrounding “rounded button defining spans” and remove its otherwise interfering default styles (no border and background colour, etc).