In this tutorial, we will learn how to create radial menus, using nothing but CSS + HTML. In a future update, I will show you how you can also create "tabs" connected to the menu. For the sake of mobile users, I will include an option for turning the radial menu into a list on smaller devices. I will also include a way to create a "pie" menu using a similar method, also in a future update.

first, a demo
Please note: I'm using custom fonts on this page, so your menu will look slightly different.

Why use a radial menu? Well, first of all, it just looks cool, especially with the use of images (example) . Radial menus are more intuitive than standard drop-down menus, and have the added benefit of being easily memorized, making them a great way to boost your website/application user experience (UX). Perhaps the only downside, is that radial menus require a lot more work on the backend, than a standard "list of items" would require.

creating the menu
the HTML code:
<div>      menu      <a class="bottom">bottom</a>      <a class="left">left</a>      <a class="right">right</a>      <a class="top">top</a> </div>
As you should notice, there's not much happening here, because most of the "work" is done in your CSS code. Where you place each menu item doesn't matter. Also, you can use ids as opposed to classes. The only limitation of using ids is that if you have multiple menus in a single site, you'll have more work to do.
the css: the menu container
.menu {      /*Required:*/      border-radius: 100%;      position: relative;      /*Recommended, but flexible:*/      width: 235px;      height: 235px;      margin: 150px auto 150px auto;      /*Optional:*/      display: flex;      justify-content: center;      align-items: center;      font-size: 35px;      letter-spacing: 3px;      color: white;      background-color: rgb(35, 35, 35);      border: 3px solid rgba(135, 135, 135, 0.8);      box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.3), inset 0px 0px 135px rgba(135, 135, 135, 0.3);      transition: 1s; } .menu:hover {      /*Optional:*/      border: 3px solid rgba(255, 255, 255, 1);      box-shadow: 0px 0px 35px rgba(0, 0, 0, 0.5), inset 0px 0px 175px rgba(255, 255, 255, 0); }
Almost everything about the menu container in this example is optional, and by optional, I mean you can tweak it to your own design. However, in order for the radial menu to work as intended, you need to keep some basic things in mind:
  • Your container should (preferably) be a true circle. You can of course create any shape you'd like, but it will make the math involved a bit more tricky to figure out, and it's not as easy to use.
  • You need an adequate margin for your menu items. I don't recommend having your menu overlap other elements, as this can cause issues with interaction and rarely, even visual artefacts.
  • Your menu should have some form of a visible hover state, so as to hint to users of your site or application that can be interacted with. This is true even if you use a label + checkbox combo(I will explain how to do this in a future update). to create a clickable menu.
the menu items:
.menu .top, .menu .left, .menu .right, .menu .bottom {      /*Required:*/      position: absolute;      opacity: 0;      z-index: 2;      /*Optional:*/      box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.3);      background-color: white;      justify-content: center;      text-decoration: none;      border-radius: 100%;      align-items: center;      font-size: 15px;      display: flex;      color: black;      width: 50%;      height: 50%; }
First, we start by setting some defaults for every item. Instead of duplicating a bunch of rules for each class, we can group them all together, and only define their exact positions and transitions separately. These have to be defined separately, or you will not have your menu items will not move, and you won't get a smooth animation.
the menu items (hover states):
.menu:hover .top, .menu:hover .left, .menu:hover .right, .menu:hover .bottom {      /*Required:*/      opacity: 1; }
This simple rule will set your menu items to be visible on hover. Now, we will give them a hover state, so that it's clear when you're about to use a item from the menu, it's actually clickable.
.menu:hover .top:hover, .menu:hover .left:hover, .menu:hover .right:hover, .menu:hover .bottom:hover {      /*Recommended, but flexible:*/      background-color: rgb(75, 75, 75);      transform: scale(0.905);      transition: 0.375s;      color: white; }
Now that we've given the menu items hover states, it's time for the real fun - making them actually move into their positions. Since we've already taken care of styling, we simply need position each item at the center of the container element, and then it's as simple as inverting their position, and using a transition to make it smooth.
/*bottom:*/ .menu .bottom {      /*Required*/      transition: 1s;      top: 25%; } .menu:hover .bottom:hover {      /*Recommended, but flexible:*/      transition: 1s;      top: -25%; }
/*Left:*/ .menu .left {      /*Required*/      transition: 1s;      top: 25%; } .menu:hover .left:hover {      /*Recommended, but flexible:*/      transition: 1s;      top: -25%; }
/*Right:*/ .menu .right {      /*Required*/      transition: 1s;      top: 25%; } .menu:hover .right:hover {      /*Recommended, but flexible:*/      transition: 1s;      top: -25%; }
/*Top:*/ .menu .top {      /*Required*/      transition: 1s;      top: 25%; } .menu:hover .top:hover {      /*Recommended, but flexible:*/      transition: 1s;      top: -25%; }

BONUS: Increase your menu's usability.
If you've made it this far, give yourself a high five! Okay, not a high five, put that crack back! Hopefully you've been testing your work along the way, and hopefully, you've realized something isn't right. Before I give away the answer, can you guess it is?
what's wrong?
Try moving between items while the menu is hovered. Notice how they collapse before you can reach them? This happens because we're reliant on the menu container being hovered in order for the menu items to be visible. We can easily fix this using a pseudo-element.
.menu::before {      /*Required*/      border-radius: 100%;      position: absolute;      content: " ";      z-index: -1;      /*Highly Recommended, but you could find another use for this element, so it's optional:*/      opacity: 0;      /*Recommended, but flexible:*/      bottom: -2.5%; /*Always use half the amount if you're going above 100% for width+height*/      transition: 1; /*If you want, you can increase this slightly.*/      height: 105%;      width: 105%; } .menu:hover:before {      /*Required*/      z-index: 1;      /*Recommended, but flexible:*/      bottom: -47.5%; /*Always use half the amount if you're going above 100% for width+height*/      height: 195%;      width: 195%; }
What is happening here?
  • Our pseudo-element acts as a kind of "buffer zone," allowing you travel between menu items (within a certain range), without losing the "attention" of the menu-container.
  • Since child elements trigger or maintain the their parent's "hover" event, the ::before pseudo-element can be used to add this functionality, without having to add anything else to your HTML code.
  • The other bonus, is that you can tweak this element so that it adds even more visual pizazz to your radial menu! For example, if you add a border, and leave the background of this element transparent, you can add an outline to the open menu, showing its physical boundaries. One drawback, however, is that you can't add a box-shadow (at least not on Chromium-based browsers), without causing rendering artefacts (I tested this).
That's it for now!
Look out for updates to land in the near future!