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.
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.
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.
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.
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.