Key Takeaways
- Rounded corners, a frequently requested CSS technique, can be achieved through various methods, including the use of CSS background images.
- CSS background images are highly versatile and can be applied to any element on a page, repeated in various ways, positioned and even fixed in place. However, CSS 2 only allows one background image per page element.
- For fixed width boxes, the challenge of applying rounded corners can be reduced to applying two background images to a div. This is achieved by exploiting the markup and applying a background image to the top of an h3 tag and another to the bottom of the containing div.
- By nesting four divs, one for each background, four backgrounds can be applied to a single div. This method, while effective, adds additional markup with no structural value.
- JavaScript and the DOM can be used to manipulate the structure of a document after it has been loaded by the browser, making it possible to dynamically append the three extra divs needed for the rounded corner effect to a single div in the source document.
Rounded corners are one of the most frequently requested CSS techniques. As with many things in CSS, there are various ways in which this problem can be approached. In this article, I’ll look at the pros and cons of some common techniques and introduce a new technique that utilises both CSS and JavaScript.
Before we dive in to the CSS, let’s remind ourselves of the old fashioned approach to this problem, which uses layout tables:
<table width="200" cellpadding="0" cellspacing="0">
<tr>
<td width="15"><img src="tl.gif" alt="" /></td>
<td bgcolor="#1b5151"></td>
<td width="15"><img src="tr.gif" alt="" /></td>
</tr>
<tr bgcolor="#1b5151">
<td> </td>
<td>Content goes here</td>
<td> </td>
</tr>
<tr bgcolor="#1b5151">
<td><img src="bl.gif" alt="" /></td>
<td></td>
<td><img src="br.gif" alt="" /></td>
</tr>
</table>
A few years ago this would have been an acceptable solution. Today, it’s an ugly hack: that’s an awful lot of redundant markup for a relatively unimportant visual decoration. In fact, the above code won’t even function as intended in documents served using a strict doctype — small gaps will appear beneath the corner images, caused by the fact that images are inline elements and, hence, leave space beneath the image for the “tails” on letters such as ‘y’ and ‘j’. The solution, as explained by Eric Meyer in Images, Tables and Mysterious Gaps, is to add the following rule to your stylesheet:
td img { display: block; }
This produces the desired result, as shown here.
But, now we’re using CSS hacks to fix ugly table hacks! Let’s look at ways to implement the same effect using only CSS.
As a general rule, any decorative image should be implemented as a CSS background image on an existing page element, rather than being dropped in to the page proper using an <img>
tag. It’s easy to determine whether an image is decorative or contains actual content: ask yourself if the absence of the image would have any effect on the overall content of the page. In the case of rounded corners, the answer is obviously not.
CSS background images are remarkably powerful things. You need only look at the many wonderful designs on display at the CSS Zen Garden for evidence of that. Using CSS, a background image can be applied to any element on a page. Furthermore, it can be repeated horizontally, vertically or not at all; it can be positioned within the background area of the image using absolute measurements, or relative to one of the four corners; it can even be made to stay fixed in place when the element’s content scrolls. Unfortunately, CSS 2 imposes one small but significant limitation: you can only apply a single background image to each element on the page. To properly render rounded corners on a <div>
we need to apply four background images, one in each corner.
Fixed Width Boxes
If the width of the box to which we’re applying decorative corners is fixed, half of the problem is solved already. If we know that the box will always be 200 pixels wide, instead of creating four background images (one for each corner), we can create two: one for the top of the box and one for the bottom. The challenge is now reduced to applying two background images to our <div>
. It’s time to take advantage of our markup.
A box with rounded corners wouldn’t be much fun if it didn’t contain any content. Consider the following:
<div class="rounded">
<h3>Exciting features!</h3>
<p>Your new Widget2000 will...</p>
<ul>
<li>... clean your shoes</li>
<li>... walk your dog</li>
<li>... and balance your cheque book!</li>
</ul>
</div>
Pretty simple, huh? The title of the box lives in an <h3>
(I’m assuming <h1>
and <h2>
have already been used further up the page’s hierarchy) and the content that follows is a paragraph and an unordered list. The key to solving our two background problem lies in the <h3>
, which comes right at the top of the box. All we have to do is to apply a background image to the top of the <h3>
, and another to the bottom of the containing <div>
, and the effect is complete:
div.rounded {
width: 200px;
background: #1b5151 url(200pxbottom.gif) no-repeat bottom center;
padding-bottom: 15px;
}
div.rounded h3 {
padding-top: 15px;
background: transparent url(200pxtop.gif) no-repeat top center;
}
Click here to see the results.
Well-structured documents are usually full of hooks like this that can be carefully exploited to apply multiple backgrounds and achieve specific visual effects. Learning to identify them is an important part of working with CSS.
Nested Elements
Applying four backgrounds to a single div is still out of our reach. But what if we nested four divs, one for each background? Doing so solves our problem, but comes at the expense of additional markup with no structural value:
<div class="rounded"><div><div><div>
Content goes here
</div></div></div></div>
And, in the CSS:
div.rounded {
width: 200px;
background: #1b5151 url(tr.gif) no-repeat top right;
}
div.rounded div {
background: transparent url(tl.gif) no-repeat top left;
}
div.rounded div div {
background: transparent url(br.gif) no-repeat bottom right;
}
div.rounded div div div {
background: transparent url(bl.gif) no-repeat bottom left;
padding: 15px;
}
The code displays as shown here.
It should be clear what’s going on here. Each of the four divs is assigned a rounded corner background image, positioned in the top-right, top-left, bottom-right and bottom-left respectively. While the width of the containing div is set to 200px, it could just as easily be set to something more flexible for use with liquid designs — the corners would still work, no matter how large or small the containing div was.
We now have a solution to the problem, which uses far less markup than the original tables example. But, it’s still not perfect: it uses three extra divs, which add nothing of value to the overall document structure. Can we do any better? It’s time to look to JavaScript.
Using the DOM
Using JavaScript and the DOM, it’s possible to manipulate the structure of a document after it has been loaded by the browser. Rounded corners are a presentational effect that can be hidden from non-JavaScript user agents without any significant reduction in their overall experience of the site, so there are no ethical problems with using JavaScript for this kind of transformation. Our final solution will require only a single <div>
in the source document. We will use JavaScript to dynamically append the three extraneous divs needed for the rounded corner effect.
Here’s the markup:
<div class="rounded">
Content goes here.
</div>
I think you’ll agree that there’s not much we can do to make it simpler than that, except maybe exchange the <div>
for a <p>
if the content is structurally better defined as a paragraph. Making this switch is left as an exercise for the reader.
Now here’s the JavaScript:
function roundedCorners() {
var divs = document.getElementsByTagName('div');
var rounded_divs = [];
/* First locate all divs with 'rounded' in their class attribute */
for (var i = 0; i < divs.length; i++) {
if (/broundedb/.exec(divs[i].className)) {
rounded_divs[rounded_divs.length] = divs[i];
}
}
/* Now add additional divs to each of the divs we have found */
for (var i = 0; i < rounded_divs.length; i++) {
var original = rounded_divs[i];
/* Make it the inner div of the four */
original.className = original.className.replace('rounded', '');
/* Now create the outer-most div */
var tr = document.createElement('div');
tr.className = 'rounded2';
/* Swap out the original (we'll put it back later) */
original.parentNode.replaceChild(tr, original);
/* Create the two other inner nodes */
var tl = document.createElement('div');
var br = document.createElement('div');
/* Now glue the nodes back in to the document */
tr.appendChild(tl);
tl.appendChild(br);
br.appendChild(original);
}
}
/* Run the function once the page has loaded: */
window.onload = roundedCorners;
The script is divided in to two logical sections. The first section iterates over all of the <div>
elements in the document, building an array of those that contain 'rounded'
in their class
attribute (remember, elements can have multiple classes separated by spaces). The second part of the script goes through each of these elements in turn, creating three additional divs and wrapping them around the original. Let’s look at the code for that in more detail:
original.className = original.className.replace('rounded', '');
Here we remove the class "rounded"
entirely from our original <div>
. The reason for this will become clear in the CSS; essentially, we don’t want the original styles applied to affect that element any more.
var tr = document.createElement('div');
tr.className = 'rounded2';
We have created out outer-most <div>
, which will be used to apply the top-right background image as well as the overall width of the box. Note that we have set the class to 'rounded2';
this will be defined in our CSS, with subtle differences from the 'rounded'
class provided to non-JavaScript-enabled clients.
/* Swap out the original (we'll put it back later) */
original.parentNode.replaceChild(tr, original);
The W3C DOM does not provide a direct method to replace a node in a document with another node. Instead, you must use the replaceChild()
method of a node to replace one of its children with another node. A useful trick to replace the node you’re looking at is to access its own parent using the parentNode
property, then use /#c#.replaceChild to swap it for something else. If that doesn’t make sense to you, don’t worry — just think of the above line as replacing our original node with the new tr
node we have just created.
/* Create the two other inner nodes */
var tl = document.createElement('div');
var br = document.createElement('div');
/* Now glue the nodes back in to the document */
tr.appendChild(tl);
tl.appendChild(br);
We’ve now created three new <div>
elements and inserted them in to the document. All that’s left is to re-insert our original node, complete with its contents:
br.appendChild(original);
At this point, our actual document tree is almost identical to that in the four nested <div>
example above, the only difference being that the outer element has a class of 'rounded2'
instead of 'rounded'
. Here’s the CSS:
div.rounded {
width: 170px;
padding: 15px;
background: #1b5151;
}
div.rounded2 {
width: 200px;
background: #1b5151 url(tr.gif) no-repeat top right;
}
div.rounded2 div {
background: transparent url(tl.gif) no-repeat top left;
}
div.rounded2 div div {
background: transparent url(br.gif) no-repeat bottom right;
}
div.rounded2 div div div {
background: transparent url(bl.gif) no-repeat bottom left;
padding: 15px;
}
Here’s the result.
The first set of rules, for div.rounded
, is only used in browsers that do not execute the JavaScript. Note that the width is 170px and the padding 15px, which adds up to a total width of 200px (the width plus the left and right padding). If you need this to work in IE 5/Windows, which interprets padding values differently, you’ll need to apply the infamous box model hack. You’ve already seen the second set of rules in the previous example.
Looking Ahead
The above technique will work in all modern browsers, and all future browsers that support the CSS2 and DOM 2 standards. CSS 3 introduces a number of new ways to achieve this effect, which will render the above techniques obsolete. As well as native rounded corner support (already available in the Mozilla family of browsers) CSS features the powerful ::outside pseudo-element, which allows additional stylable elements to be inserted in a manner similar to the JavaScript example shown in this article. If that’s not enough, border images will allow pretty much any border decoration you could care to think of.
Unfortunately, it will be years before CSS 3 support is widely available. Until then, JavaScript is more than capable of taking up some of the slack.
Frequently Asked Questions (FAQs) about CSS and JavaScript Rounded Corners
How can I create rounded corners with CSS?
Creating rounded corners with CSS is quite simple. You can use the ‘border-radius’ property to achieve this. The ‘border-radius’ property can have one, two, three, or four values. A single value will apply the same radius to all four corners. Two values will apply the first radius to the top-left and bottom-right corners, and the second radius to the top-right and bottom-left corners. Three values will apply the first radius to the top-left, the second to the top-right and bottom-left, and the third to the bottom-right. Four values will apply a different radius to each corner, in the order top-left, top-right, bottom-right, bottom-left.
For example, to apply a 10px radius to all corners of a div, you would use:div {
border-radius: 10px;
}
Can I create rounded corners with JavaScript?
Yes, you can create rounded corners with JavaScript, although it’s more common to use CSS for this purpose. If you need to create rounded corners dynamically based on user input or other factors, JavaScript might be the better choice. You can use the ‘CanvasRenderingContext2D.roundRect()’ method to draw a rectangle with rounded corners on a canvas. This method takes five parameters: the x and y coordinates of the top-left corner of the rectangle, the width and height of the rectangle, and the radius of the corners.
How can I create a circle with CSS?
Creating a circle with CSS is a special case of creating rounded corners. If you set the ‘border-radius’ property to 50%, the corners will be rounded enough to form a circle. The element also needs to have equal width and height. For example:div {
width: 100px;
height: 100px;
border-radius: 50%;
}
This will create a div that is a perfect circle.
Can I create rounded corners on only one side of an element?
Yes, you can create rounded corners on only one side of an element by specifying different values for the ‘border-radius’ property. For example, to round only the top-left and top-right corners of a div, you would use:div {
border-radius: 10px 10px 0 0;
}
This will apply a 10px radius to the top-left and top-right corners, and no radius (i.e., a square corner) to the bottom-left and bottom-right corners.
How can I create a border with rounded corners?
Creating a border with rounded corners is just as easy as creating an element with rounded corners. You simply apply the ‘border-radius’ property to the element with the border. For example:div {
border: 1px solid black;
border-radius: 10px;
}
This will create a div with a 1px black border and rounded corners with a 10px radius.
Can I create rounded corners with different radii on the same element?
Yes, you can create rounded corners with different radii on the same element by specifying different values for the ‘border-radius’ property. For example, to create a div with a 10px radius on the top-left corner, a 20px radius on the top-right and bottom-left corners, and a 30px radius on the bottom-right corner, you would use:div {
border-radius: 10px 20px 20px 30px;
}
Can I create elliptical corners with CSS?
Yes, you can create elliptical corners with CSS by specifying two values for each corner in the ‘border-radius’ property. The first value is the horizontal radius, and the second value is the vertical radius. For example:div {
border-radius: 10px 20px;
}
This will create a div with elliptical corners, with a horizontal radius of 10px and a vertical radius of 20px.
Can I animate the ‘border-radius’ property?
Yes, you can animate the ‘border-radius’ property using CSS transitions or animations. For example, to gradually change the border radius from 0 to 50% over a period of 2 seconds, you could use:div {
border-radius: 0;
transition: border-radius 2s;
}
div:hover {
border-radius: 50%;
}
This will cause the div’s corners to become rounded when you hover over it, and the change will occur gradually over 2 seconds.
Can I use percentages for the ‘border-radius’ property?
Yes, you can use percentages for the ‘border-radius’ property. The percentage is relative to the corresponding dimension of the element. For example, if you set ‘border-radius’ to 50%, the corners will be rounded enough to form a circle (or an ellipse, if the element’s width and height are not equal).
Can I create rounded corners on images?
Yes, you can create rounded corners on images by applying the ‘border-radius’ property to the img element. This can be a nice effect for thumbnails or profile pictures. For example:img {
border-radius: 10px;
}
This will apply a 10px radius to the corners of all images.