Heygrady (there's a new blog)

I don't get It.

Cross-Browser Rounded Corners Overview

Permalink

Theres no shortage of techniques for achieving cross-browser rounded corners. Most of the techniques involve taking an otherwise simple box and adding markup and CSS until all browsers (IE 6+, Firefox, Safari, Chrome) agree to round the corners. Of course the great hope today is to use CSS3 techniques such as border-radius or multiple backgrounds and of course those approaches won't work in Internet Explorer 8 or below. Ironically, one of the best places to find some of the old school CSS solutions is on the MSDN page describing border-radius . For modern browsers, the border-radius property will do a good job of rounding corners. To support all browsers some clever techniques will need to be used.

For the uninitiated

Goals

To successfully approximate border-radius, the content of the box should appear as it does in a regular box with a border applied.

Simple Box
(view live example)
  • Start with a simple box
  • The box's content should appear similarly with or without border-radiussupport
  • The solution should work in all browsers (IE6 is a bonus) with or without border-radius support
  • The solution should scale with the content and obey the box
    • with fluid height/width
    • with fixed height/static width
  • Bonus Points: easy to implement

A Simple Box

As mentioned above, border-radius is supported in roughly 50% of browsers globally (as of February, 2011). The other 50% of browsers are IE 8 and below.

box.htmlGist page
<div class="box">
<h2>Some Headline</h2>
<p>My content.</p>
</div>

Above is the simple box that we want to give rounded corners. The goal is to have it look something like the picture below.

simple box with a red border
Border Radius
(view live example)
border-radius-box.cssGist page
/* border radius */
/* Firefox, Safari, Chrome, IE9 */
.box {
-moz-border-radius: 12px;
-webkit-border-radius: 12px;
border-radius: 12px;
border: 1px solid red;
}

Again, in all browsers (except IE 8 and below) the above CSS will add rounded corners to any .box. According to the Mozilla border-radius documentation the -moz- and -webkit- prefixes are for older versions of Firefox and Safari respectively. IE 9, Firefox 4, Safari 5 and Chrome 4+ all support the standard W3C border-radius property without the vendor prefix.

Polyfills

With the above goals in mind a straight-forward solution might be to force IE 8 and below into line using some creative JavaScript-based technique that won't require any images to be created.

CSS3 PIE

CSS3 PIE an extremely clever polyfill technique that reads and processes the CSS file using a DHTML behavior in Internet Explorer to find the border-radiusrules and then generates a VML object, placing it behind the target element. A tool like CSS3 PIE can be used to support rounded corners in IE 8 and below. The big drawback to CSS3 PIE is that it works best on static content and is slow to render. Because the VML object is placed behind the box, it must remove all of the visible styles from the box including border and background images. Those visible styles are then applied to the VML object.

CSS3 PIE (IE 8 and below)
(view live example)

View the page above in IE 8 or below and the corners should look correct. Refresh (hit F5) repeatedly and see that the corners do not appear instantly. Inspect the HTML in IE 8 or below (hit F12) and see the VML object. It's worth noting that CSS3 PIE isn't particularly slower than any other similar polyfill.

Pros:

  • Easy to implement
  • Targets IE8 and below only
  • Closely approximates native CSS3 rounded corners

Cons:

  • Slow:
  • Fragile:
    • If the element visibility is toggled with JavaScript, CSS3 PIE will have issues.
    • CSS3 PIE can cause other unexpected display issues, such as visual discrepancies when zooming in IE7.

jQuery Corner

There are also tools like jQuery Corner and Curvy Corners that will use native border-radius in supported browsers and use a series of 1px tall div elements to polyfill in other browsers. This is a popular solution to the problem that works by adding in extra div elements. Each div is given a right and left border the same color as the page behind the element and the border widths are altered to create appearance of a rounded corner.

Pros:

  • Easy to implement
  • Falls back to native CSS3 for rounded corners in supported browsers

Cons:

  • Support for borders doesn't work well in IE (oops!)
  • The corners cover the content within the box (this could be fixed)
  • No anti-aliasing in Internet Explorer. Each pixel of the rounding is a solid color due to the way the rounding effect is achieved
  • Over a dozen other virtually useless corner templates included in the package

Background Images

If native border-radiusisn't an option (and polyfills aren't stable enough), then it will take several images to do the trick. In some cases a sprite can be used. For the examples below we'll try to create the same 1px red border with a 12px border-radiusfrom above. Rounded corner images can easily be created at a number of on-line generators. My favorite is the Rounded Corner Image Generator.

  • corner-tl.png — top left corner
  • corner-t.png — top border
  • corner-tr.png — top right corner
  • corner-r.png — right border
  • corner-br.png — bottom right corner
  • corner-b.png — bottom border
  • corner-bl.png — bottom left corner
  • corner-l.png — left border
  • corners.png — a rounded corner sprite. All of the corners and borders are combined into one image.

As is evident by the top, bottom, left and right images, when the border is a simple 1px solid color, those images may not be necessary and could be replaced by actual borders. For these examples images were used anyway because it's more common that the borders are complex and need to be images — particularly when shadows are involved.

Onion Skinning And Sliding Doors

Most rounded corner techniques are a variation on the classic Onion Skinned Drop Shadow article or the Sliding Doors article (part 2). For instance, one of the techniques from the MSDN article uses a variant of the sliding doors technique. These techniques are effective but they're not particularly easy to implement. They're also limited because they try to accommodate IE6 which has a fair share of documented CSS issues. There is no shortage of cross browser rounded corner articles. The majority mention either using a method similar to CSS3 PIE or jQuery Corner above or they recommend using an image + HTML + CSS solution like the ones below.

  • Typing out multiple nesting div tags can be tedious and difficult to replicate across many pages on a large site.
  • The most bullet-proof solutions would require as many as 8 wrapping divs (top, bottom, left, right, top-left, top-right, bottom-right, bottom-top) to properly imitate the capabilities of border-radius.
  • Specifying complicated HTML and CSS layouts limits the situations in which it can be used.

Using Onion Skinning

Onion skinning is the process of adding multiple wrapping div elements to a .box element and then applying a different background image to each of the layers. This technique was originally developed for creating drop shadows but can easily be adapted to creating rounded corners.

Onion Skin
(view live example)

Pros:

  • Very flexible and simple CSS
  • Can easily be scaled back to not require the top, bottom, left and right div elements
  • Easy to understand HTML
  • Content is naturally on top of the background images, no need to use relative positioning to avoid content clipping
  • Works in all browsers, including IE6

Cons:

  • The .content needs to have a height set if it is applied to a fixed-height box
  • The corner images cannot be transparent because they need to cover the edges of the box
  • Very ugly markup
  • Cannot use a CSS sprite for corners or sides because of how CSS backgrounds are specified
onion-box.htmlGist page
<div class="box top">
<div class="bottom">
<div class="left">
<div class="right">
<div class="top-left">
<div class="top-right">
<div class="bottom-right">
<div class="content bottom-left">
<h2>Some Headline</h2>
<p>My content.</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

The HTML markup above shows how onion skinning would work on the original simple box from the beginning of the article. Each layer of the onion contains the background image for one of the sides or corners. It's possible to re-use the .box and .content elements which results in only 7 extra wrapping div elements. The sides are wrapped first so that the corners can cover them up later.

onion-box.cssGist page
/* Onion Skinned */
/* All browsers */
.box {
padding: 1px; /* account for missing border */
}
.box .bottom {
margin: -1px;
}
.box .content {
padding: 1px; /* account for missing border, stretch to fit content */
margin: -1px -1px -1px 0;
height: 200px; /* duplicate box height */
}
/* background images */
.box.top {
background: url(corner-t.png) left top repeat-x;
}
.box .bottom {
background: url(corner-b.png) left bottom repeat-x;
}
.box .left {
background: url(corner-l.png) left top repeat-y;
}
.box .right {
background: url(corner-r.png) right top repeat-y;
}
.box .top-left {
background: url(corner2-tl.png) left top no-repeat;
}
.box .top-right {
background: url(corner2-tr.png) right top no-repeat;
}
.box .bottom-right {
background: url(corner2-br.png) right bottom no-repeat;
}
.box .bottom-left {
background: url(corner2-bl.png) left bottom no-repeat;
}

The accompanying onion skinned CSS is very straightforward. Each layer gets its own background image and it is positioned appropriately. The sides repeat and the corners do not.

  • For fixed-height boxes, the .box height (200px in this case) should be duplicated on the .content
  • To replicate the look of a box with a border, padding should be used to space content away from the edges (for a 4px border, user 4px padding, etc.)
  • To account for how collapsing margins would interact with a real border, padding-top and padding-bottom need to be set on the .content

Using Sliding Doors

The sliding doors technique uses a content area sandwiched between a top and a bottom element. The top and a bottom each contain elements for left and right corners. This technique allows for great flexibility with using image sprites and allows the corners to be fully alpha transparent if desired. The markup appears to be much more sensible but the CSS is much more complicated.

Sliding Door
(view live example)
Sliding Door using a Sprite
(view live example)

Pros:

  • Allows for image sprites and alpha transparent corners
  • Sensible HTML markup
  • Works in all browsers, including IE6

Cons:

  • More complex CSS
  • For fixed height boxes, extra calculations are required to account for padding on the top and bottom of the box
sliding-door-box.htmlGist page
<div class="box">
<div class="top">
<span class="top-left"></span>
<span class="top-right"></span>
</div>
<div class="left">
<div class="right">
<div class="content">
<h2>Some Headline</h2>
<p>My content.</p>
</div>
</div>
</div>
<div class="bottom">
<span class="bottom-left"></span>
<span class="bottom-right"></span>
</div>
</div>

In the sliding doors example, a few extra div elements are added to the top and bottom of the box and the content is wrapped in some left and right div elements. This allows the left and right backgrounds to grow with the height of the content. In a case where the left and right border is a simple solid color these wrappers may be eliminated and the border applied directly to the .content.

sliding-door-box.cssGist page
/* Sliding Door */
/* All browsers */
.box {
position: relative;
padding: 13px 1px; /* account for missing border, doctor 100% height */
height: 178px; /* 202 - 24, account for excess padding */
}
.ie6 .box {
height: 178px !important; /* 202 - 24, account for excess padding */
}
.box .content {
position: relative;
z-index: 2;
margin: -12px 0; /* pull up content */
}
.box .top, .box .bottom {
position: relative;
z-index: 1;
height: 12px;
margin: 0 11px;
font-size: 0;
line-height: 0;
}
.box .top {
margin-top: -13px; /* Pull up top */
}
.box .bottom {
margin-bottom: -15px; /* Pull down bottom */
}
.box .top-left, .box .top-right, .box .bottom-left, .box .bottom-right {
position: absolute;
z-index: 1;
height: 12px;
width: 12px;
font-size: 0;
line-height: 0;
}
.box .left, .box .right {
height: 100%;
}
.box .right {
padding: 1px 0; /* stretches margins */
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
padding-right: 1px;
margin-right: -1px;
}
.box .left {
padding-left: 1px;
margin-left: -1px;
}
.ie6 .box .left, .ie7 .box .left, .ie6 .box .right, .ie7 .box .right {
position: relative;
z-index: 2;
}
.ie6 .box .right, .ie7 .box .right {
padding: 0 1px 0 0;
width: 100%;
margin: 0;
margin-left: -1px;
right: -2px;
}
.ie6 .box .left, .ie7 .box .left {
padding: 0;
margin: 0;
left: -1px;
}
.box .top-left, .box .bottom-left {
left: -12px;
}
.box .top-right, .box .bottom-right {
right: -12px;
}
/* background images */
.box .top {
background: url(corner-t.png) left top repeat-x;
}
.box .top-left {
background: url(corner-tl.png) left top no-repeat;
}
.box .top-right {
background: url(corner-tr.png) right top no-repeat;
}
.box .bottom {
background: url(corner-b.png) left bottom repeat-x;
}
.box .bottom-left {
background: url(corner-bl.png) left top no-repeat;
}
.box .bottom-right {
background: url(corner-br.png) right bottom no-repeat;
}
.box .left {
background: url(corner-l.png) left top repeat-y;
}
.box .right {
background: url(corner-r.png) right top repeat-y;
}

To correctly position all of the pieces, the box is given a top and bottom padding the same height as the corner (12px in this case). For a fixed height box this padding should be subtracted from the original box height. Then the .top and .bottom containers are given a top and bottom margin of -12px respectively (the same as the top and bottom padding on the box). The .top and .bottom containers are also given left and right margins the same as the width of the corners (again, 12px in this case). The corners are then absolutely positioned into the empty space created by giving them a -12px left and right position respectively.

The .left and .right wrappers wrap the .content and operate more like the onion skinning techniques. The right div is given a 1px top and bottom padding to account for collapsing margins and the .content is given a negative margin to pull its edges to the edge of the box. Adding position relative and a z-index ensures that the .content is above the top and bottom div containers to avoid content clipping.

The left and right wrappers are particularly challenging because of the need to apply a 100% height to ensure they are the correct height in conjunction with the 1px top and bottom border applied to prevent collapsing margins from affecting the layout. This isn't an issue in IE 6 and 7 because they collapse the top margin even when there's a border. But for the other browsers the 1px border combined with a 100% height on the .right wrapper makes it 2px too tall. By using an alternate method for spacing in IE 6 and IE 7, the padding can be negated by adjusting the box-sizing.

Using Absolute Positioning

A solution that is not often posited is using absolute positioning to place all of the corners and sides on the box. This solution takes advantage of how most browsers (except IE 6) handle conflicting right and left properties on an element. For instance, a rule like .top {position: absolute; left: 12px; right: 12px;} effectively stretches .top across the full width of its nearest positioned parent. IE 6 ignores the conflicting rules which makes it more difficult to support that browser with this technique.

Pros:

  • Very simple CSS
  • Very simple HTML
  • Supports fluid and fixed height/width boxes automatically
  • Supports dynamic content

Cons:

  • Doesn't work in IE 6
Absolute Position
(view live example)
Absolute Position using a Sprite
(view live example)
position-box.htmlGist page
<div class="box">
<span class="top"></span>
<span class="bottom"></span>
<span class="left"></span>
<span class="right"></span>
<span class="top-left"></span>
<span class="top-right"></span>
<span class="bottom-right"></span>
<span class="bottom-left"></span>
<div class="content">
<h2>Some Headline</h2>
<p>My content.</p>
</div>
</div>

In the example above, the 8 corner and side spans are added to the top of the .box and the content of the box is wrapped in a .content div. The .content is to allow for proper layering so that the corners are below the content.

position-box.cssGist page
/* Position */
/* Firefox, Safari, Chrome, IE7+ */
.box {
position: relative;
padding: 1px; /* account for missing border */
}
.box .content {
position: relative;
z-index: 2;
}
/* Corners and Sides */
.box .top-left, .box .top-right, .box .bottom-right, .box .bottom-left, .box .top, .box .bottom, .box .left, .box .right {
position: absolute;
z-index: 1;
font-size: 0;
line-height: 0;
}
/* Sides */
.box .top, .box .bottom {
left: 12px;
right: 12px;
height: 12px;
}
.box .left, .box .right {
top: 12px;
bottom: 12px;
width: 12px;
}
.ie7 .box .left, .ie7 .box .right {
bottom: 10px; /* Accounts for padding issue in IE 7 */
}
.box .top {
top: 0;
background: url(corner-t.png) left top repeat-x;
}
.box .bottom {
bottom: 0;
background: url(corner-b.png) left bottom repeat-x;
}
.box .left {
left: 0;
background: url(corner-l.png) left top repeat-y;
}
.box .right {
right: 0;
background: url(corner-r.png) right top repeat-y;
}
/* Corners */
.box .top-left, .box .top-right, .box .bottom-right, .box .bottom-left {
height: 12px;
width: 12px;
}
.box .top-left {
top: 0;
left: 0;
background: url(corner-tl.png) left top no-repeat;
}
.box .top-right {
top: 0;
right: 0;
background: url(corner-tr.png) right top no-repeat;
}
.box .bottom-right {
bottom: 0;
right: 0;
background: url(corner-br.png) right bottom no-repeat;
}
.box .bottom-left {
bottom: 0;
left: 0;
background: url(corner-bl.png) left bottom no-repeat;
}

The CSS for absolute positioning is really straight-forward. The sides and corners are all set to be position: absolute and given z-index: 1 while the .box and .content are set to position: relative and the .content is given z-index: 2. The corner spans are positioned in the appropriate corner and the sides are positioned on the appropriate side. The key trick is giving, for instance, the .top a left: 12px and a right: 12px. The apparently conflicting riles actually stretch the span the full width of the .box. The same is done with the .left and .rightsides, giving them both top and bottom properties to stretch them the full height of the .box. The 12px is to allow space for the corners to avoid overlap.

Ignoring Legacy Browsers

Using Border Radius

As mentioned at the start of this article, border-radius can be used to add rounded corners to a .box.

Using Multiple Backgrounds

It's now possible to use multiple backgrounds in newer browsers including Firefox 3.6+, IE 9, Safari and Chrome. Multiple backgrounds are layered in in the order they are added in the CSS with the first image being the top-most layer. Using this technique for corners might seem slightly silly given the existence of border-radius in every browser that supports multiple backgrounds, however there are times when the native border-radius cannot accomplish the same thing as images.

Multiple Backgrounds
(view live example)

Pros:

  • No extra markup required
  • Easy to understand CSS
  • Can be used to create custom graphic borders that border-radius cannot support
  • Can be combined with mark-up based solutions and simple JavaScript to support legacy browsers

Cons:

  • Won't work in IE 8 or below
  • Won't work in Firefox 3.5 or below
  • Cannot be used with sprites (but inline images would work)
background-box.cssGist page
/* multiple backgrounds */
/* Firefox 3.6+, Safari, Chrome, IE 9 */
.box {
background:
url(corner2-tl.png) left top no-repeat,
url(corner2-tr.png) right top no-repeat,
url(corner2-br.png) right bottom no-repeat,
url(corner2-bl.png) left bottom no-repeat,
url(corner-t.png) left top repeat-x,
url(corner-b.png) left bottom repeat-x,
url(corner-l.png) left top repeat-y,
url(corner-r.png) right top repeat-y;
padding: 1px; /* account for missing border */
}

Conclusion

While CSS3 PIE is promising, it is still difficult to use on large scale sites. The JavaScript-based polyfill techniques don't seem to work very well in IE 8 and below at the moment (this is extremely unfortunate). Most of the techniques for applying backgrounds require ugly markup or intensely difficult CSS to accomplish. I personally prefer the Absolute Positioning technique as it is the easiest to implement and offers some support of Legacy browsers. The fact that IE 6 is not supported is not a huge issue given that IE 6 is no longer necessary to support for most projects (4.6% globally, 2% in the US and Europe as of February 2011).

In the future I intend to create a simple jQuery plug-in that will apply the absolute positioning technique to a .box.

  • border-radiusSupported in only 50% of browsers as of February 2011
  • Multiple Backgrounds — Supported in only 46% of browsers as of February 2011
  • CSS3 PIE - Interesting technique with limitations on large scale sites.
  • jQuery Corners - Doesn't work as intended. Could probably be fixed but no one has.
  • Onion Skinning - Ugly HTML, easy CSS, works in 100% of browsers
  • Sliding Doors - Cleaner HTML, uglier CSS, works in 100% of browsers
  • Absolute Positioning - Clean HTML, easy CSS, doesn't work in IE6

Comments