A List Apart

Menu
Issue № 165

CSS Design: Creating Custom Corners & Borders

by Published in CSS, Graphic Design96 Comments

We’ve all heard the rap:

“Sites designed with CSS tend to be boxy and hard-edged. Where are the rounded corners?”

Answer: the rounded corners are right here. In this article, we’ll show how customized borders and corners can be applied to fully fluid and flexible layouts with dynamic content, using sound and semantically logical markup.

The markup

In the example markup below, XHTML line breaks have been inserted to pad out dummy paragraphs:

<h2>Article header</h2>

<p>
A few paragraphs of article text.<br />
A few paragraphs of article text.
</p>

<p>
A few paragraphs of article text.<br />
A few paragraphs of article text.
</p>

<p>
A paragraph containing author information
</p></code></pre><h2>The hooks</h2>If we want full control of the layout, we need to make sure we have enough elements we can target with our CSS. Let’s call these elements “hooks.” Our markup needs just a few more.First of all, let’s wrap the whole article in a containing <code title="structural division">div</code>, and then wrap each structural section in an appropriate element:<pre><code><strong>
<div class="Article">
  <h2>Article header</h2>  <div class="ArticleBody">
    <p>
      A few paragraphs of article text.<br />
      A few paragraphs of article text.
      </p>    <p>
      A few paragraphs of article text.<br />
      A few paragraphs of article text.
      </p>
    </div>
  <div class="ArticleFooter">
  <p>
    A paragraph containing author information
    </p> 
    </div>
  </div>

 
If we examine the markup, we’ll see that we have given ourselves at least five hooks, which is all we need to place customized graphics in each of the four corners (and left side) of our article.

See Step 1primary markup.

The design

First let’s decide on some basic layout parameters. Our graphic designer gave us this mockup for reference:“I want the borders and corners to look something like this,” he said. He also told us to be aware of the fact that all articles may have different widths and heights, and that he still wasn’t sure what kind of background he wanted the articles to have. In fact, he wasn’t even sure those were the borders he wanted.

“Could you leave that open, or make it so that it’s easy to change?” he asked.

The process

We intend to keep the number of hooks as low as possible, so we’ll have to pay extra attention when we start to prepare the images for our solution, and make sure that the graphics we need are suitable to be hooked up to elements already present in our document.

We have a div containing the whole article. That’ll do for our top left corner and top and left sides. Header elements are block-level by default, and we’ll take advantage of their behavior: they extend to the full width of their parent element. So we’ll use the <h2> element for our top right corner.

We’ll use our article-footer div for the bottom left corner — and the contained paragraph for our bottom right corner.

Step 1.1 shows how we slice up the sketch.

Note: Obviously, you can use any element to hook graphics up with. Your document’s markup is unlikely to exactly match the structure used in our example. For all we know, you may only have a single paragraph of text to which you hope to apply customized corners and borders. You can easily do so.As stated earlier, all you need is at least four structural elements. (Depending on the height of your element you may require five.)

If necessary, these elements could be nonsemantic divs, each with its own class. Just remember that for a div element to be rendered, it must contain content to manifest its presence. Also keep in mind that if your content lends itself to common structural elements such as headers, paragraphs, and so on, you can and should use those instead of relying on nonsemantic divs.

The styles

To continue, let’s turn on element borders and set a relative width for the div that contains the whole article, to see how things behave:

div.Article {
  width:35%;
  border: 1px solid red; }
div.Article h2 {
  border: 1px solid blue; }
div.ArticleBody {
  border: 1px solid black; }
div.ArticleFooter {
  border: 1px solid blue; } 
div.ArticleFooter p {
  border: 1px solid magenta; }

 

See Step 2basic element behaviour

 
  Nothing really surprising here. We do, however, take notice of the gaps appearing before and after our div class=“ArticleBody”. Ignoring that problem for now, we’ll go on and write ourselves a style sheet:

body {
  background: #cbdea8;
  font: 0.7em/1.5 Geneva, Arial, Helvetica, sans-serif;
  }
div.Article {
  background: 
 url(images/custom_corners_topleft.gif)
  top left no-repeat;
  width:35%;
  }
div.Article h2 {
  background: 
 url(images/custom_corners_topright.gif) 
  top right no-repeat;
  }
div.ArticleBody {
  background: 
 url(images/custom_corners_rightborder.gif) 
  top right repeat-y;
  }
div.ArticleFooter {
  background: 
 url(images/custom_corners_bottomleft.gif) 
  bottom left no-repeat;
  }
div.ArticleFooter p {
  background: 
 url(images/custom_corners_bottomright.gif) 
  bottom right no-repeat;
  }

See Step 3first attempt.

Not bad at all! Actually better than we expected. Obviously we need to add some padding to our respective elements to make the layout look better — and then there are those pesky gaps to fix. The gaps are caused by the carriage returns inserted by our paragraph (block) elements. We could avoid using paragraph elements altogether and thereby bypass the problem, but — for reasons well-known to ALA readers —  we prefer to keep our markup structurally clean and logical. It isn’t our data’s fault that we are lazy stylers.

In our first pass, we assumed that a carriage return must equal 1.5em, as that was the value we specified for our line-height. Therefore our first attempt was to add a margin-top:-1.5em to our ArticleBody and ArticleFooter. It worked perfectly in most standards-compatible browsers — all except the ones used by the 94% of internet users on this planet (no names, please).After testing, trial, error, rinse, and repeat we find that we must use at least a margin-top:-2em to be sure that the elements touch and the gap closes:

div.Article {
  background: 
 url(images/custom_corners_topleft.gif) 
  top left no-repeat;
  width:35%;
  }
div.Article h2 {
  background: 
 url(images/custom_corners_topright.gif) 
  top right no-repeat;
  font-size:1.3em;
  padding:15px;
  margin:0;
  }
div.ArticleBody {
  background: 
 url(images/custom_corners_rightborder.gif) 
  top right repeat-y;
  margin:0;
  margin-top:-2em;
  padding:15px;
  }
div.ArticleFooter {
  background: 
 url(images/custom_corners_bottomleft.gif) 
  bottom left no-repeat;
  }
div.ArticleFooter p {
  background: 
 url(images/custom_corners_bottomright.gif) 
  bottom right no-repeat;
  display:block;
  padding:15px;
  margin:-2em 0 0 0;
  }

Step 4 looks like we’re finally there!

Backward compatibility?

If you’ve been viewing this example in Netscape 4.x, you’ve probably noticed that the page shows up blank. We’ve found no way to get this technique to work acceptably in NS 4.x, so instead we’re going to hide the styles that the browser in question cannot render properly. NS 4.x does not understand style tags with media=“all” specified and we’ve taken advantage of that in the example that follows. We’ve made two style tags, one with styles we want all browsers to render, and another we intend to hide from NS 4.x. Even though it breaks our heart to do so, we’ve also changed our font size specification from ems to pixels. You wanted backward compatibility — you’ve got it.

Step 5graceful degradation in NS 4.x

The real world

“Yeah — but we want to see real-world applications, mate,” you say. We anticipated that and provided an example of the technique applied in a more advanced context. We borrowed an already thoroughly tested layout from Alex Robinson, and applied our styles to it — and we’re glad we did!Our first attempt unleashed a cavalcade of calamities in IE6/Win, triggering bugs affecting z-index stacking level of our elements. Entire elements disappeared; margins acted like children kept up long past their bedtime. It was a mess.

Then we learned that a simple position:relative and a well positioned <br /> could fix everything. View the source in Step 6 for further investigation.

Step 6our attempt at applying our technique to a full-fledged layout with headers, columns, and footers

Limitations

If you have been paying attention, you probably realize this example only plays well with surrounding, solid-color backgrounds. With this method we need to cover the graphics from the top left corner with the graphics in the top right corner.

If we made the top right corner graphic transparent, the top left graphic beneath it would show. Same goes for the bottom. And that is indeed a limitation. Perhaps in a Part II edition of this article we will show how to work with gradient backgrounds.Meanwhile, this article demonstrates a generic method, with backward compatibilty and sound markup in mind, and it is our sincere hope that this will inspire a lot of offspring and ideas — perhaps even some that avoid the need to work with solid background colors.

Acknowledgements

Brian Alvey for discussions, insisting on graceful degradation and real world examples, and David Schontzler  for helping a Danish dude write technical text in English.

Editor’s note

While we were preparing this ALA issue for publication, designer Ryan Thrash came up with a nearly identical approach to the problem of creating rounded CSS boxes based on semantically correct markup. Thrash and Madsen came up with their approaches independently of each other; both authors acknowledge the influence of Douglas Bowman’s previous ALA articles, Sliding Doors of CSS (20 October 2003) and Sliding Doors of CSS II (30 October 2003).Independently of all that, ALA systems designer Brian Alvey previously crafted a different approach to rounded corners, which may be seen at Weblogs, Inc. It’s all good. — Ed.

96 Reader Comments

Load Comments