Rounded Corners and Borders

May 8, 2009
I dislike rounded corners and rounded borders. It seems like whenever business owners want to “update” their web sites but they don’t have any useful functionality improvements in mind, they resort to asking for rounded corners. To be clear, I don’t particularly hate the visual design choice of rounded corners; what I dislike is the implementation annoyance of rounded corners in practice. Until a majority of browser installs (read most common Internet Explorer version) fully supports CSS3, implementing rounded corners will never be ROI positive. Of course, negative ROI rarely stops business owners. So here’s one possible rounded corners implementation that seems to work across most browser types and versions and is fairly reasonable to maintain. In CSS3, the way you create rounded corners is by listing multiple images and positions of those images for background. With CSS3, you’re no longer limited to only a single background image per DOM element. So the HTML for a CSS3 implementation could be as simple as:
<div class="rounded blue box_type_a">
    <h4>Rounded Edge Blue Background</h4>
    <p>This is a simple rounded edge <div>.</p>
</div>
The “rounded” class is where in the CSS I’d define the four different corners (or maybe a fifth if I wanted a centered background image). The other two classes setup visual style and physical container box dimensions. You can of course push these three of these into a single class, but having them separated like this lets you mix and match, inheriting cascading styles, which is sort of the whole point. Anyway, starting with the above HTML, which seems fairly reasonable to maintain, I wanted to make a CSS-less-than-3 implementation that kept code badness to a minimum. So the first thing is that in a CSS-less-than-3 world, we need to have four “things” inside the <div> to which we can attach background images; namely, the four corners. As much as I dislike doing this, it seems like JavaScript needs to come to the rescue.
function addRoundedCornersSupport() {
    var corners = new Array(
        "top_left", "top_right",
        "bottom_left", "bottom_right"
    );
    var divs    = document.getElementsByTagName("div");

    for ( var i = 0; i < divs.length; i++ ) {
        var classes = divs[i].className.split(" ");
        for ( var j = 0; j < divs.length; j++ ) {

            if (
                classes[j] == "rounded" ||
                classes[j] == "bordered"
            ) {

// for each <div> that has the class of either "rounded" or
// "bordered", embed four <div>s, one for each corner
for ( var k = 0; k < corners.length; k++ ) {
    var corner = document.createElement("div");
    corner.className = "corner_" + classes[j] + " " + corners[k];

    // this is where we prepare the corner <div> and
    // append it to the <div> that should have corners
    // (see below)
}

            }
        }
    }
}
This function, which we’ll run on page load, finds every <div> that has the class name of “rounded” or “bordered”. (I’ll explain what that second class does later.) If the <div> has that class, we’re going to add four <div>s to it, each with appropriate class names we can style in CSS. Now enter the first of a few IE6 hacks. It seems all other modern browsers (browsers that are in active use by a non-trivial percentage of users) can position an empty <div> with explicit height according to a bottom range without ignoring the height setting. IE6 will collapse the height. I truly hate the stupid that is IE6. Anyway, to overcome this, and only for IE6, you have to add the corner graphics into the bottom two <div>s instead of just styling them in with CSS. Otherwise, all you need is to add a space into the <div>.
// if the browser is IE6, the bottom left and right corners
// must have the image explicitly added using an embedded
// <img> tag instead of relying on CSS
if (
    navigator.appVersion.indexOf("MSIE 6") != -1 &&
    classes[j] == "bordered" &&
    corners[k].substr( 0, 6 ) == "bottom"
) {
    var borderImg    = document.createElement("img");
    borderImg.width  = "14";
    borderImg.height = "14";
    borderImg.alt    = "";
    borderImg.src    = corners[k] + "_border.gif";
    corner.appendChild(borderImg);
}
else {
    corner.appendChild( document.createTextNode(" ") );
}

divs[i].appendChild(corner);
Now we need to call this when the page loads, but it’s bad to do that by setting window.onload because there’s a good chance in a complex site there will be some overwriting of that setting. Instead, we’re going to attach and event or listener.
// the following block causes the
// addRoundedCornersSupport function to run on
// page load without overriding 
// any other onLoad or attached events
if ( window.attachEvent ) {
    window.attachEvent(
        "onload", addRoundedCornersSupport
    );
}
else if ( window.addEventListener ) {
    window.addEventListener(
        "load", addRoundedCornersSupport, false
    );
}
else {
    document.addEventListener(
        "load", addRoundedCornersSupport, false
    );
}
At this point, we only need to add some CSS to finish off the implementation. All we need to do is add some basic styling for rounded <div>s and the internal corners we added.
div.rounded {
    background-color: #cccccc;
    padding: 15px;
    position: relative;
}

div.rounded div.corner_rounded,
div.rounded div.corner_bordered {
    position: absolute;
    width: 14px;
    height: 14px;
    /*
        prevents older browsers from 
        forcing a height > 14px
    */
    line-height: 1px;
}

div.rounded div.top_left {
    top: 0px;
    left: 0px;
    background-image: url( top_left.gif );
}

div.rounded div.top_right {
    top: 0px;
    right: 0px;
    background-image: url( top_right.gif );
}

div.rounded div.bottom_left {
    left: 0px;
    /*
        This removes a 1px
        underhang at the bottom
    */
    bottom: -1px;
    background-image: url( bottom_left.gif );
}

div.rounded div.bottom_right {
    right: 0px;
    /*
        This removes a 1px
        underhang at the bottom
    */
    bottom: -1px;
    background-image: url( bottom_right.gif );
}
That’s basically it. Of course, you’ll want to add styles for “blue” and similar style classes and “box_type_a” and similar structural CSS classes. OK, so I promised to explain what the “bordered” class was all about. Well, as annoying as the implementation of rounded corners is, having a rounded corners with a border is even more annoying. I went through several different approaches to solving this problem until I hit on this one, which seems to work in every browser (including IE6) and isn’t too terrible to maintain. Again, let’s start with the HTML:
<div class="rounded blue box_type_a">
    <div class="rounded bordered white">
        <h4>Rounded Edge Bordered White Background</h4>
        <p>
            This is a rounded edge blue bordered,
            white background block.
        </p>
    </div>
</div>
I’m not the biggest fan of this. Personally, I’d rather have this be all one <div>. I guess with a bit more JavaScript, we could implement this with a single <div> with a key class name. I opted to have this be two <div>s to keep the JavaScript a bit more sane. And looking at purely the HTML, it seems to make sense having the two . Still, I can see the argument to go the other way. So with this, look back at the JavaScript and you’ll notice how the “bordered” section picks up corners as well but with different classes. The CSS to implement those classes is as follows:
div.bordered {
    margin: -14px;
    position: relative;
}

div.rounded div.bordered div.top_left {
    background-image: url( top_left_border.gif );
    _left: -15px; /* IE6 only */
}

div.rounded div.bordered div.top_right {
    background-image: url( top_right_border.gif );
    _right: 1px; /* IE6 only */
}

div.rounded div.bordered div.bottom_left {
    background-image: url( bottom_left_border.gif );
    _left: -15px; /* IE6 only */
    _bottom: 1px; /* IE6 only */
}

div.rounded div.bordered div.bottom_right {
    background-image: url( bottom_right_border.gif );
    _bottom: 1px; /* IE6 only */
    _right: 1px;  /* IE6 only */
}
Unfortunately, you’ll see here I had to include quite a lot of IE6-only styles. I hate doing that, but otherwise, this is going to look like garbage in IE6. Of course, you’ll need the graphics for the four corners (and the corners for the border corners), but I figure that’s fairly obvious how to put together. I decided on 14 pixel tall and wide graphics for fairly pronounced corners. Other implementations may want smaller corners, maybe 8 to 10 pixels tall and wide. You can view the full example implementation to see all the pieces working together.

Comments (2)

Xeno wrote:

Have you tried doing this in jQuery? There is a very simple addon that drastically reduces your js called jcorners.

http://malsup.com/jquery/corner/

Trevor Hall wrote:

This is the best blog in the entire world. If I had a blog, I would copy and paste everything you write. I love reading blogs.

Post a Comment

After posting a comment, it will have to be approved before it's displayed on the site.

View from WebCam

Current View from Home

Recent Posts

Blog Categories

Archives

Syndication Feeds

Contact Information

J. Gryphon Shafer
AIM: ShaferGryphon
ICQ: 63254641
YIM: GryphonShafer
Skype: gryphon.shafer
Jabber: GryphonShafer
MSNIM: gryphonshafer
PerlMonks: gryphon
CPAN: gryphon
PGP ID: CBE9C7F0

Presentation