Use CSS to create stylish circular navigation

Give your site a visually appealing animated circle navigation using the power of CSS transforms and transitions


Get started

To get started, let’s create a new HTML5 file and add in some default HTML within the body. We are going to add in a container <div> called ‘wrapper’ and then make sure we leave a comment at the ending </div> just to keep things clean and tidy. Then we simply create two empty script tags positioned at the very bottom of our file.

001 <body>
002 <div class=”wrapper”>
003 </div><!-- END wrapper -->
004 <script src=””></script>
005 <script src=””></script>     
006 </body>

Container and heading

Now inside our wrapper <div>, let’s add another <div> and give it a class name of ‘container’. Within that we can add in a page title using the <h2> tag, which of course we will style later on to make it look larger and more appealing across our page.

001 <div class=”container”>
002    <h2>Web designer magazine</h2>
004 </div><!-- End Container -->

Button and navigation

In this step we are going to add in a button that we can click to activate our menu. We are going to give it both a class name and ID name so that we can style it and use a touch of JavaScript later on to add some functionality. After this, we create an unordered list with seven list items and again give this both a class and ID name.

001 <button class=”button” id=”button”>Menu</    button>
002 <div class=”nav-wrapper” id=”nav-wrapper”>
003          <ul>
004             <li></li>
005             <li></li>
006             <li></li>
007             <li></li>
008             <li></li>
009             <li></li>
010             <li></li>                      
011        </ul>
012 </div><!-- End Nav -->

Populate the navigation

Now we can populate our list items with empty anchor tags wrapped around some text. It’s completely up to you regarding what you want to put in here, but be aware that using long words will break it. We can then include a <span> tag on every list item that will allow us to point to the text using some CSS.

001  <ul>
002  <li><a href=”#”><span>Home</span></a></    li>
003  <li><a href=”#”><span>About</span></a></    li>
004  <li><a href=”#”><span>Portfolio</    span></    a></li>
005  <li><a href=”#”><span>Blog</span></a></li>
006  <li><a href=”#”><span>Tutorials</span></    a></li>
007  <li><a href=”#”><span>Contact</span></    a></    li>
008  <li><a href=”#”><span>Follow</span></a></    li>
009  </ul>


To finish off our HTML, let’s add in a couple of scripts at the bottom of our file, just above the </body> tag. We have included these scripts for you, but they are located on the resource CD as it’s a bit beyond the scope of this tutorial with exactly how they are written. However, we will quickly go through what they do in a later step.

001 <script src=”js/polyfills.js”></script>
002 <script src=”js/scripts.js”></script> 

Default styles

Now we create a new CSS file called ‘styles.css’ and at the top add in some default styles. First we are going to target all elements and make sure that they are positioned relative, before checking that ‘box-sizing’ is set to ‘border-box’. What means is that any padding or border specified on any element can be laid out and drawn inside its specified width and height.

001 * {
002    position: relative;
003    box-sizing: border-box;
004    list-style: none;
005    margin: 0;
006    padding: 0;
007 }
008 html, body {
009    height: 100%;
010 }


In this step we’re going to add in some styles to our container <div>. This will span across the entire page and hold our page title within. First then, let’s make sure it’s set to relative. After that we want to give it a bit of height, before setting the background to black and then turning down its opacity to 0.09.

001 .container {
002    position: relative;
003    margin-bottom: 2em;
004    height: 15em;
005    background: rgba(0,0,0,0.09);
006    font-family: Arial, sans-serif;
007 }

Page title

At this stage we’re going to improve the look of our page title by applying some styles to it. First of all we make sure it’s positioned absolute and anything overflowing is hidden. Then we give it some width and make sure it’s centred. We also want to make the effect a little more subtle by setting the opacity to 0.5.

001 .container > h2 {
002    position: absolute;
003    overflow: hidden;
004    width: 100%;
005    text-align: center;
006    text-transform: uppercase;
007    white-space: nowrap;
008    font-size: 9em;
009    opacity: 0.5;
010    cursor: default;
011    padding: 15px;
012 }

Menu button

Now we can create our main clickable menu button. We need to position this absolute and use a percentage for the top and left properties. Further down we give it the same width and height, but then set the radius to 50% so as to create a circle. Finally, we make sure the outline is set to ‘none’, which gets rid of the yellow border after it’s been clicked within Chrome.

001 .button {
002    position: absolute;
003    top: 100%;
004    left: 50%;
005    z-index: 11;
006    margin-top: -2.25em;
007    margin-left: -2.25em;
008    padding-top: 0em;
009    width: 4.5em;
010    height: 4.5em;
011    border: 5px solid rgba(0,0,0,0.05);
012    border-radius: 50%;
013    background: none;
014    background-color: #fff;
015    color: #1780b4;
016    text-align: center;
017    font-weight: 700;
018    font-size: 1.5em;
019    text-transform: uppercase;
020    cursor: pointer;
021    -webkit-backface-visibility: hidden;
022    outline: none;
023 }

The navigation

Here we’re going to target the whole navigation, positioning it and applying some styles. We’re going to use the same positioning and radius as the button but make the width a lot bigger. We then make sure it’s hidden by setting the opacity to zero, and then giving it some animation by using ‘transition’ and ‘transform’.

001 .nav-wrapper {
002    position: absolute;
003    top: 100%;
004    left: 50%;
005    z-index: 10;
006    margin-top: -13em;
007    margin-left: -13.5em;
008    width: 27em;
009    height: 27em;
010    border-radius: 50%;
011    background: transparent;
012    opacity: 0;
013    transition: all .3s ease 0.3s;
014    transform: scale(0.1);
015    pointer-events: none;
016    overflow: hidden;
017 }

Less clickable areas

In this next CSS rule, we are going to create a cover over the menu to prevent any extra space becoming clickable. Without this, when you roll your cursor over the white space in between each navigation button, it becomes clickable and we don’t want this. Let’s put a red border around this to see what we mean after completing the next step – but be sure to remove it later.

001 .nav-wrapper:after{
002  content:”.”;
003  display:block;
004  font-size:2em;
005  width:6.2em;
006  height:6.2em;
007  position: absolute;
008  left: 50%;
009  margin-left: -3.1em;
010  top:50%;
011  margin-top: -3.1em;
012  border-radius: 50%;
013  z-index:10;
014  color: transparent;
015  border: 1px solid red; /* remove this */
016 }

Opened navigation

This is where we get to see some action. This rule will allow us to open up our navigation and create a 50% circle. The opacity is set to 1 so that it is visible once the button is clicked on, and the transition is set to three milliseconds. We would recommend that you play around with the transition time, perhaps doubling it to six milliseconds so that it animates slightly slower.

001 .opened-nav {
002    border-radius: 50%;
003    opacity: 1;
004    transition: all .3s ease;
005    transform: scale(1);
006    pointer-events: auto;
007 }

Navigation list items

In this step we’re going to add some styles to our list items. However, we won’t actually see much until we complete Step 14. What we are doing here is positioning all of our <li> items at the top, stacked on top of one another. We’re also using ‘transition’ for the animation of each one and ‘transform’ to give us their shape.

001 .nav-wrapper li {
002    position: absolute;
003    top: 50%;
004    left: 50%;
005    overflow: hidden;
006    margin-top: -1.3em;
007    margin-left: -10em;
008    width: 10em;
009    height: 10em;
010    font-size: 1.5em;
011    transition: all .3s ease;
012    transform: rotate(76deg) skew(60deg);
013    transform-origin: 100% 100%;
014    pointer-events: none;
015 }

 The anchors

Now we can see even more action by adding some styles to our anchor tags. Again, we need to make sure our position is set to absolute, and then we use ‘bottom’ and ‘right’ properties. After giving them some width and height, we can then give them some colour. We now make sure that they are skewed and rotated to the same degree as our list items, but using a negative value.

001 .nav-wrapper li a {
002    position: absolute;
003    right: -7.25em;
004    bottom: -7.25em;
005    display: block;
006    width: 14.5em;
007    height: 14.5em;
008    border-radius: 50%;
009    background: #429a67;
010    background: radial-gradient(transparent     35%, #1780b4 35%);
011    color: #fff;
012    text-align: center;
013    text-decoration: none;
014   font-size: 1.2em;
015    line-height: 2;
016    transform: skew(-60deg) rotate(-76deg)     scale(1);
017    backface-visibility: hidden;
018    pointer-events: auto;
019 }

Navigation button text

In this step we are going to target the <span> tag and style our navigation button text. This is pretty straightforward and you don’t need a detailed explanation, however just be cautious with which font and size you use. We set the font family in an earlier step, but it pays to experiment with this where you can.

001 .nav-wrapper li a span {
002    position: relative;
003    top: 1.8em;
004    display: block;
005    font-size: .45em;
006    font-weight: 700;
007    text-transform: uppercase;
008 }

Hover, active and focus

Since we are almost done with our list items, one of the last steps would be to give them a hover, active and focus state. So, what we’re going to do is keep the gradient on hover, but darken the colour slightly. We’re also going to keep to this colour on the active and focus states.

001 .nav-wrapper li a:hover,
002 .nav-wrapper li a:active,
003 .nav-wrapper li a:focus {
005    background: radial-gradient(transparent     35%, #1775a4 35%);
006 }

Opened-nav class

Here we’re creating a rule with a class that our JavaScript (‘scripts.js’) file will use. This will determine when the navigation is open and dynamically include the class – then we set the speed at which it opens. At the moment however, we can’t see that happening until we complete the final few steps – so let’s power through!

001 .opened-nav li {
002   transition: all .3s ease .3s;
003 }

First and second child

When the ‘.open-nav’ class has been added by our script, we need a way to spread the list items out. Of course, the only way to do that is to transform and rotate them. So in this rule, we target the first child and the second child and rotate and skew them accordingly.

001 .opened-nav li:first-child {
002    transform: rotate(-20deg) skew(60deg);
003 }
005 .opened-nav li:nth-child(2) {
006    transform: rotate(12deg) skew(60deg);
007 }

The other children

Let’s continue rotating our navigation buttons and target the other list items within our navigation list. You may notice we are skewing them all by the same degree (60deg) but using a different degree on the rotate. Again, we would encourage you to have a play around with these numbers so you can see how this works.

001 .opened-nav  li:nth-child(3) {
002    transform: rotate(44deg) skew(60deg);
003 }
004 .opened-nav li:nth-child(4) {
005    transform: rotate(76deg) skew(60deg);
007 }
008 .opened-nav li:nth-child(5) {
009    transform: rotate(108deg) skew(60deg);
010 }
012 .opened-nav li:nth-child(6) {
013    transform: rotate(140deg) skew(60deg);
014 }
016 .opened-nav li:nth-child(7) {
017    transform: rotate(172deg) skew(60deg);
018 }

 Responsive navigation

One of the last things we need to do is to make our navigation responsive. We all know responsive web design is a major part of our workflow these days, and this is no exception. First we’re going to target the tablet (iPad) by setting the ‘max-width’ to 600px, which will change the padding on our ‘nav-wrapper’.

001 @media only screen and (max-width: 600px) {
002    .nav-wrapper {
003        padding: .5em;
004    }
006 }

 Finish up

Now let’s make sure our navigation is responsive for mobile phones. We need to change the ‘max-width’ to 480px; when we reach this size we need to change the font size so it doesn’t break our navigation. Finally we do the same with the font on our navigation button by targeting the ‘.button’ class.

001 @media only screen and (max-width: 480px) {
002    .nav-wrapper {
003        font-size: .68em;
004    }
005    .button {
006        font-size: 1em;
007    }
008 }

Final thoughts

Following along to a tutorial like this is a sure-fire way to get a good foothold on CSS transforms and transitions. However, it is highly recommended that you experiment with transforms and learn more about the mathematics involved, which while help you to really master it all. Once you’ve done that you can create some truly stunning animations for your own projects.