Round corners. Something that should be easy, right? Not so much. It seems they were left out of the CSS 2.1 specification. No matter, there are a couple of ways to hack them out and make them look good. This post contains several solutions with their pro's and con's. All mentioned solutions have support for modern browsers and anti-aliasing.
Every element in HTML is square. You can apply a background image to an element to make it look round, but the element itself is still square.
CSS3 comes with a heaven-sent solution: border-radius. It allows you to apply a rounded corner or border to an element without the use of extra markup or Javascript. Unfortunately, only Gecko (Firefox's rendering engine) and Webkit (Safari, Chrome) support it, with prefixed equivalents -webkit-border-radius and -moz-border-radius, because their support is still experimental.
Webkit and Gecko have a market share of 20% and 7%. What to do with the other 73%, mainly Internet Explorer 6 and 7? We don't want to serve the majority square corners, while only a minority sees the beautiful round corners, right?
And what about Opera? Wasn't that browser pretty modern? Well, Opera supported -o-border-radius in 2006 in version 9 of their browser, but they removed the support in 9.1 for unknown reasons.
So we need a cross-browser solution. Fortunately, there are a few:
Adding markup
The most straight-forward solution to create rounded corners on a box is by adding extra markup (html) to the box to apply corner backgrounds to. With a fixed width and height, you only need one element to show four rounded corners. With a fixed width and dynamic height, you need at least two elements. With a dynamic width and dynamic height, you need four.
In most cases, you won't need extra elements for types 1 and 2. For example, when you have markup that already looks like this:
<div>
<h3>Title</h3>
<p>Text</p>
</div>
If only the height is dynamic, the complete top (with the top-left and top-right rounded corners) can be applied to the h3, which overlaps the div. The div itself has a very tall background image positioned from the bottom, with the bottom-left and bottom-right corners and the filling for the middle.
With a dynamic height and dynamic width and using the same markup, you don't have enough elements to apply a corner to. The div can contain the top-left corner and the filling, the h3 can have the top-right corner, but then we're still two elements short, so we'll need to add two more elements to show the bottom corners.
Our new markup:
<div>
<h3>Title</h3>
<p>Text</p>
<span class="bottom-left"></span>
<span class="bottom-right"></span>
</div>
Now we can add the bottom background images to the spans, but our beautiful clean html is cluttered.
Making it dynamic
To keep our markup clean, we could add a little Javascript. Let's take our first markup snippet again, and add a class to it:
<div class="rounded">
<h3>Title</h3>
<p>Text</p>
</div>
We could now tell Javascript to add four span's to all elements with the class "rounded". We want to get this result:
<div class="rounded">
<span class="top-left"></span>
<span class="top-right"></span>
<h3>Title</h3>
<p>Text</p>
<span class="bottom-left"></span>
<span class="bottom-right"></span>
</div>
This transformation only takes a few simple lines of code, especially when using jQuery. With our Javascript-generated corner spans, we have enough elements to apply corners to, and we could generate general styles using the classes which are the same for every rounded box.
A small note: if the background below the div has a pattern or gradient, you need even more elements so they aren't overlapping eachother.
Pro's
- Fast and light-weight
Con's
- Javascript required (but with fallback to square corners)
- No support for gradient backgrounds
- Requires one image per color foreground/background combination
- Some of Anymeta's Javascript may not work anymore because it doesn't expect the markup to be modified.
Also, use caution using this method with existing sites. If you have any scripts that don't expect your site's markup to be modified by another script, it may break stuff.
Making it super-dynamic
Until now, our solutions were depending on using a background image. So for every combination of border-radius, foreground color and background color, you'll need a seperate image. It would be a lot easier if this was done completely automagic. You could write a back-end script, creating the images dynamically, but that would cost a lot of time.
Fortunately, there are cross-browser solutions that don't need any images to work. In this area we find two free Javascript libraries: CurvyCorners and ShadedBorder. They work by dynamically creating hundreds of tiny divs in each corner. Together, these divs form an anti-aliased rounded corner.
Pro's:
- Support for gradient backgrounds
- No images needed
Con's
- Javascript required (but with fallback to square corners)
- Doesn't support rounded corners on form input fields and hyperlinks
- The creation of so many elements could make page generation and scrolling slow when having lots of rounded corners
- Some of Anymeta's Javascript may not work anymore because it doesn't expect the markup to be modified.
The effect is super, and both CurvyCorners and ShadedBorder are easy to setup and configure. There's a major cause for concern, however. If you want a box to have four rounded corners with a border-radius of 20 pixels and a border of 3 pixels, CurvyCorners creates 424 extra div's, each with their own position, background-color and transparency. With 5 rounded boxes, that's 2120 extra div's. That could cause serious performance issues with page buildup, scripts and scrollspeed, especially in older browsers.
Our solution
$.fn.rounded_corners = function()
{
return this.each(function()
{
$(this).css({position: 'relative'});
$('<span class="cnrs cnr_nw"></span><span class="cnrs cnr_ne"></span><span class="cnrs cnr_sw"></span><span class="cnrs cnr_se"></span>')
.css({ // this will only accept numeric border widths
marginTop: '-' + (parseInt($(this).css('border-top-width')) || 0),
marginRight: '-' + (parseInt($(this).css('border-right-width')) || 0),
marginBottom: '-' + (parseInt($(this).css('border-bottom-width')) || 0),
marginLeft: '-' + (parseInt($(this).css('border-left-width')) || 0)
})
.appendTo(this);
// ie odd values bug fix
if( $.browser.msie && $.browser.version < 7)
{
$(this).each(function()
{
var h = $(this).height();
var w = $(this).width();
if(h%2)
{
$('.cnr_sw', this).css({bottom: '-1px'});
$('.cnr_se', this).css({bottom: '-1px'});
}
if(w%2)
{
$('.cnr_ne', this).css({right: '-1px'});
$('.cnr_se', this).css({right: '-1px'});
}
});
}
});
}
if($.browser.msie)
{
// add rounded corners to these elements for IE - for the rest we use -moz-border-radius or -webkit-border-radius
$('.selector1, .selector2, #selector3').rounded_corners();
}
met:
Reacties
Blaise
Tim
Ana