Tabs with Round Out Borders

Avatar of Chris Coyier
Chris Coyier on (Updated on )

DigitalOcean provides cloud products for every stage of your journey. Get started with $200 in free credit!

Rounded corners are now trivially easy to achieve via border-radius. But that only allows us to cut into the shape. What if we want to connect a shape to another with a rounded outward corner. Much easier to explain with a graphic:

The top corners are easy, just border-radius. The bottom corners, less easy.
View Demo   Download Files

Clean HTML

Of course, on the web, just about anything visual is possible. Worst-case-scenario you can use images. Our goal here, as ever, is to use no images (quick! accessible! easy to update!) and use completely clean semantic HTML (quick! accessible! easy to update!). So here’s the markup:

<ul class="tabs group">
  <li class="active"><a href="#one">One</a></li> 
  <li><a href="#two">Two</a></li> 
  <li><a href="#three">Three</a></li>
  <li><a href="#three">Four</a></li> 
</ul>

A class of active indicates which tab reflects the current page.

How this is going down

The reason this is tricky is that we need a shape to stick out of the tab element. To get this done while keeping our markup clean, we’ll use pseudo elements. If you need a refresher, you can learn about them here and here. Essentially, they can add extra elements to the page that we can style, directly through CSS. Every element can have two – :before and :after. Ultimately we’ll be using four per tab, which is possible because each tab is make from two elements, the list item and the anchor link.

Let’s visualize this step by step, without looking out any code just yet.

1) Natural State

List items are naturally block level and anchor links are inline, so the layout is like this.

2) Float

By floating the list items to the left, the list items will line up next to each other and shrink to the size of the anchor links inside them.

3) Same Size

The list items themselves have no margin or padding, so really the list items and anchor links are the exact same size, directly on top of each other.

4) Just one

Let’s focus on just one of them…

5) Circles

6) Squares

With the other two pseudo elements we’ll make smaller squares.

7) Colorize the tab and content

The “active” tab and the content will share the same background color.

8) Colorize the pseudo elements

The squares match the color of the active tab, the circles match the background behind them.

9) Stacking

With z-index, we’ll make sure the circle sits on top and cuts off the color of the square.

10) No borders

The borders were just for illustration, really it would look more like this.

11) Finishing

Add the same concept to the outer tabs and round the tops with border-radius and it’s done!

CSS

This is a big ass block of CSS, but I’ve tried to comment it up so that each part makes sense.

.tabs li { 
  /* Makes a horizontal row */
  float: left; 
	
  /* So the psueudo elements can be
     abs. positioned inside */
  position: relative; 
}
.tabs a { 
  /* Make them block level
     and only as wide as they need */
  float: left; 
  padding: 10px 40px; 
  text-decoration: none;
  
  /* Default colors */ 
  color: black;
  background: #ddc385; 
  
  /* Only round the top corners */
  -webkit-border-top-left-radius: 15px;
  -webkit-border-top-right-radius: 15px;
  -moz-border-radius-topleft: 15px;
  -moz-border-radius-topright: 15px;
  border-top-left-radius: 15px;
  border-top-right-radius: 15px; 
}
.tabs .active {
  /* Highest, active tab is on top */
  z-index: 3;
}
.tabs .active a { 
  /* Colors when tab is active */
  background: white; 
  color: black; 
}
.tabs li:before, .tabs li:after, 
.tabs li a:before, .tabs li a:after {
  /* All pseudo elements are 
     abs. positioned and on bottom */
  position: absolute;
  bottom: 0;
}
/* Only the first, last, and active
   tabs need pseudo elements at all */
.tabs li:last-child:after,   .tabs li:last-child a:after,
.tabs li:first-child:before, .tabs li:first-child a:before,
.tabs .active:after,   .tabs .active:before, 
.tabs .active a:after, .tabs .active a:before {
  content: "";
}
.tabs .active:before, .tabs .active:after {
  background: white; 
  
  /* Squares below circles */
  z-index: 1;
}
/* Squares */
.tabs li:before, .tabs li:after {
  background: #ddc385;
  width: 10px;
  height: 10px;
}
.tabs li:before {
  left: -10px;      
}
.tabs li:after { 
  right: -10px;
}
/* Circles */
.tabs li a:after, .tabs li a:before {
  width: 20px; 
  height: 20px;
  /* Circles are circular */
  -webkit-border-radius: 10px;
  -moz-border-radius:    10px;
  border-radius:         10px;
  background: #222;
  
  /* Circles over squares */
  z-index: 2;
}
.tabs .active a:after, .tabs .active a:before {
  background: #ddc385;
}
/* First and last tabs have different
   outside color needs */
.tabs li:first-child.active a:before,
.tabs li:last-child.active a:after {
  background: #222;
}
.tabs li a:before {
  left: -20px;
}
.tabs li a:after {
  right: -20px;
}

That’s that!

View Demo   Download Files

Should work in just about any decent browser and also IE 9 and up. Should also fall back fine (just no round-outs) in older browsers.

Steve Smith Method

About the exact time as I was creating this to include in a talk I was doing about pseudo elements. Steve Smith of Ordered List published a very similar method. Steve’s method has borders around the tabs, mine has tabs butted up against each other. Both cool if you ask me.