This is an archive site. For the recent posts, visit orderedlist.com.

Ordered List

OrderedList

May 4 2011

Flared Borders with CSS

Ever wondered how to create an element that flares into another using only CSS? Here’s a quick tutorial using existing CSS attributes, in combination with CSS generated content, to produce the effect using no images at all, and no additional markup. And it falls back gracefully for older browsers.

Let’s start with a mockup of what we want to build. And, if you want to jump right to the end, here’s the finished demo for you to examine.

Flared Borders Mockup

So first, we start with our markup. For this demo, it’s a simple unordered list inside an HTML page.

<!doctype html>
<html>
  <head>
    <title>CSS3 Flared Borders</title>
  </head>
  <body>
    <ul>
      <li class="current"><a href="#">Home</a></li>
      <li><a href="#">About</a></li>
      <li><a href="#">Products</a></li>
      <li><a href="#">Services</a></li>
      <li><a href="#">Contact</a></li>
    </ul>
  </body>
</html>

Once that’s in place, we can start with our styles. I’m going to style up the basic, non-flared navigation bar to start, and we’ll add the flares next.

html {
  background:#efefef;
}

body { margin:0; padding:0; color:#222; font:13px "Helvetica Neue", Helvetica, Arial, sans-serif; }

ul { position:absolute; left:0; right:0; top:0; height:30px; background:#959DA5; border-bottom:1px solid #333; margin:0; padding:10px 16px 0; list-style:none; }

ul li { float:left; margin:0 20px 0 0; padding:0; }

ul a { display:block; color:#fff; text-decoration:none; padding:0 15px; line-height:29px; height:29px; font-weight:bold; background:#464646; border:1px solid #333; border-bottom:none; -webkit-font-smoothing:antialiased; -webkit-border-top-left-radius:8px; -webkit-border-top-right-radius:8px; -moz-border-radius:8px 8px 0 0; border-top-left-radius:8px; border-top-right-radius:8px; text-shadow:#000 0 -1px 0; / This is to prevent jagged borders in Webkit. / -webkit-background-clip: padding-box; }

ul li.current a { background:#efefef; color:#222; height:30px; text-shadow:#fff 0 1px 0; / This is to prevent jagged borders in Webkit. / -webkit-background-clip: padding-box; }

After that styling, we end up with this.

Non-flared styling

Adding a bit of Flare

Our next step is to recreate the flare effect. We do this as a combination of two steps. First, we cover up the bottom left and right outside edges with the same background color as our tab. Because we don’t want to put markup in our HTML for this effect, we’ll use CSS generated content, which has surprisingly good browser support1. 1 Quirksmode has more information on CSS before and after psudo elements.

So let’s grab the :before and :after of our list item, and create these blocks. Note, that because I’m positioning these psudo-elements absolutely, I’ll need to position the list items relatively to keep them in line.

ul li {
  position:relative;
}

ul li:before, ul li:after { content:''; width:9px; height:8px; position:absolute; z-index:2; bottom:0; background:#464646; }

ul li:before { left:-8px; }

ul li:after { right:-8px; }

ul li.current:before, ul li.current:after { background:#efefef; bottom:0; -webkit-background-clip: padding-box; }

Let’s walk through that a little. First, I select both the :before and :after psudo elements, and set their content to an empty string, so they’ll appear on the page. Next, I position them to the bottom of the li, set the width and height to something slightly larger than our border radius, and set the background color to the same as the tab. Next, I position each :before and :after separately, sending the :before to the left, and the :after to the right. The result looks something like this.

Starting the Flare

The next step here is to cover up these new blocks with another set of :before and :after psudo elements (this time from the a) colored the same as the ul background, and use a border radius on the bottom corner to ‘reveal’ the tab color beneath it. Then we apply the same border color to the bottom and left/right of the element to blend in with our border on the list item, and the bottom of the list itself.

ul a {
  position:relative;
}

ul a:before, ul a:after { content:''; width:10px; height:8px; position:absolute; z-index:3; bottom:-1px; background:#959DA5; overflow:hidden; border-bottom:1px solid #333; -webkit-background-clip: padding-box;

}

ul a:before { left:-11px; border-bottom-right-radius:8px; -webkit-border-bottom-right-radius:8px; -moz-border-radius-bottomright:8px; border-right:1px solid #222; }

ul a:after { right:-11px; border-bottom-left-radius:8px; -webkit-border-bottom-left-radius:8px; -moz-border-radius-bottomleft:8px; border-left:1px solid #222; }

ul li.current a:before, ul li.current a:after { bottom:0; }

And that’s all there is to it. The finished product.

The finished product

And the great part about this, is it falls back gracefully in any browser that doesn’t support border-radius, without have to do any form of detection or JS sniffing. Simple and easy.

Try it for yourself. Here’s the final demo for you to explore.