Crafting Minimal Circular 3D Buttons with CSS

Avatar of Brandon Pierce
Brandon Pierce on (Updated on )

📣 Freelancers, Developers, and Part-Time Agency Owners: Kickstart Your Own Digital Agency with UACADEMY Launch by UGURUS 📣

The following post is written by Brandon Pierce. Brandon saw some nicely designed buttons by Wouter on Dribbble and set about building them with CSS. They came out nicely and he agreed to post about the process here.

Here’s what we are going to make in this tutorial:

View Demo   Download Files

The Base HTML

We will be using a simple unordered list1 within a <nav> tag to create our buttons.

  <ul class="nav">
    <li><a href="#" class="icon-home"></a></li>

    <li><a href="#" class="icon-cog"></a></li>

    <li><a href="#" class="icon-cw"></a></li>

    <li><a href="#" class="icon-location"></a></li>



Some Resources

  • These icons in the buttons come from the Fontello icon font and web app.
  • The textured background is from Subtle Patterns.

CSS for the Icon Font

There are a few ways to go about this, but we’ll go with this:

@font-face {
  font-family: 'fontello';
  src: url('../fonts/fontello.eot');
  src: url('../fonts/fontello.eot?#iefix') format('embedded-opentype'),
       url('../fonts/fontello.woff') format('woff'),
       url('../fonts/fontello.ttf') format('truetype'),
       url('../fonts/fontello.svg#fontello') format('svg');
  font-weight: normal; font-style: normal;

[class*=" icon-"] {
  font-family: 'fontello';
  font-style: normal;
  font-size: 3em;
  speak: none;

.icon-home:after     { content: "\2302"; } 
.icon-cog:after      { content: "\2699"; } 
.icon-cw:after       { content: "\27f3"; } 
.icon-location:after { content: "\e724"; }

CSS for the Buttons

The buttons will be arranged horizontally by making the list items inline-block. We’ll make them butt up against each other with a little negative margin (or see these other techniques).

.nav {
  list-style: none;
  text-align: center;

.nav li {
  position: relative;
  display: inline-block;
  margin-right: -4px;

The lovely three dimensionality of the buttons comes from a subtle gradient and bevel and embossing courtesy of a pair of box-shadows, one normal and one inset. The width and height of the buttons are identical creating a square, which turns to a circle with border-radius. The line-height matches the height and text-align is set to center, so the icons will be both horizontally and vertically centered.

.nav a {
  display: block;
  background-color: #f7f7f7;
  background-image: -webkit-gradient(linear, left top, left bottom, from(#f7f7f7), to(#e7e7e7));
  background-image: -webkit-linear-gradient(top, #f7f7f7, #e7e7e7); 
  background-image: -moz-linear-gradient(top, #f7f7f7, #e7e7e7); 
  background-image: -ms-linear-gradient(top, #f7f7f7, #e7e7e7); 
  background-image: -o-linear-gradient(top, #f7f7f7, #e7e7e7); 
  color: #a7a7a7;
  margin: 36px;
  width: 144px;
  height: 144px;
  position: relative;
  text-align: center;
  line-height: 144px;
  border-radius: 50%;
  box-shadow: 0px 3px 8px #aaa, inset 0px 2px 3px #fff;

We can flatten out the buttons a bit on :hover to create a pressed look. You may want to save that for :active and do something else for :hover, but we’ll leave that up to you.

.nav a:hover{
  text-decoration: none;
  color: #555;
  background: #f5f5f5;

At this point our buttons should look like this:

Not bad, but we have a ways to go.

Now on to the fun part, were going to be using pseudo elements to give us some additional elements to work with to create the rest of the effects. To create the horizontal “groove” you see between each button were going to add the pseudo selector :before to our list items. Using negative z-index we can keep it beneath everything. The double border give it the inset look. The negative margins we used on the list items earlier are what allow this groove to look continuous. We take advantage of absolute positioning to center the line.

.nav li:before {
  content: "";
  display: block;
  border-top: 1px solid #ddd;
  border-bottom: 1px solid #fff;
  width: 100%;
  height: 1px;
  position: absolute;
  top: 50%;
  z-index: -1;

For that extra three-dimensionality, we’ll create a “well” around each anchor that the button sit inside. We can create this again with no additional HTML by using a pseudo element.

.nav a:before {
  content: "";
  display: block;
  background: #fff;
  border-top: 2px solid #ddd;
  position: absolute;
  top: -18px;
  left: -18px;
  bottom: -18px;
  right: -18px;
  z-index: -1;
  border-radius: 50%;
  box-shadow: inset 0px 8px 48px #ddd;

The size of the well (a bit larger than the button itself) comes from absolutely positioning itself behind the button and stretching itself out by using negative positioning values (top/bottom/left/right). The 3D look this time comes from a top border and inset box-shadow creating an internal shadow.

And we’re done!

That wasn’t so bad was it?

View Demo   Download Files

Browser Support

These buttons use a good bit of CSS3 and some pseudo elements which are CSS 2.1. Nothing too overly progressive though. Any version in the last few years of Safari, Chrome, Opera, or Firefox will have no trouble. IE 8 doesn’t do CSS3, so it falls back to this:

Not a big deal.

IE 7 is similar only without the “groove” line as it doesn’t do pseudo elements. Also not a big deal.


1 Unordered lists may not actually be ideal for navigation. We still used them here because we needed the extra element.