“Radial” navigation menu examples have been popping up all over the web like mushrooms after rain recently, inspiring some of my students to ask how they are achieved. After reviewing the wide swath of various menus out there, I found that most are JavaScript-heavy and not terribly accessible, causing me to create my own version. In particular, I wanted to make a radial menu meet three goals:

  1. HTML markup and JavaScript should be minimal;
  2. CSS animation should be used wherever possible;
  3. Accessibility should be ever-present.

Keeping Things Simple With Semantic Ligature Icon Fonts

The first step was a utilitarian decision: rather than complicating the markup with bitmap images or , I decided to use SymbolSet’s excellent “Social Circlesemantic ligature icon fonts. While the term might be a mouthful, the concept is simple: by setting up the embedded fonts and markup correctly, I could translate each link into smooth, precise icons. The markup is:

 <nav class="ss-icon">
	<a href="#">Twitter</a>
	<a href="#">Facebook</a>  
	<a href="#">Pinterest</a  
	<a href="#">Google+</a>   
	<a href="#">Ello</a>
</nav>

Stacking Elements With CSS

Next, I needed to present the <nav> element as a round button:

nav { 
	position: absolute;
	width: 7rem; 
	height: 7rem; 
	line-height: 7rem;
	border-radius: 50%;
	transition: .3s;
	background: #f00;
}

And for the links inside that element:

a { 
	width: 5rem; height: 5rem;
	border-radius: 50%;
	background: red;
	text-align: center;
	line-height: 1.5;
	color: #fff;
	text-decoration: none;
	position: absolute;
	font-size: 4rem;
	text-align: center;
	left: 1rem; 
	top: 1rem; 
	transition: .4s;
}

This placed the links all in the same position on top of the <nav> element. The problem was that I wanted each of them to be hidden underneath the <nav>. This is actually trickier than one might suppose: child elements will always appear above their parent element by default, even after you try giving them a negative z-index value:

a {
	z-index: -1;
}

The solution: while keeping the negative z-index in place, add a declaration to the parent element:

nav { 
	z-index: initial;
}

This places the links underneath the hamburger menu.

Arraying Elements With Sass

With the links in place, I could turn to distributing them in a circle. This is where JavaScript is usually called upon in other radial menus, where it is employed to array the icons and make them move at the correct angle. But that’s easy with a Sass loop:

@for $i from 1 through 6 {
	$rot: (($i - 2) * 72);
	a:nth-child(#{$i}) {
		transform: rotate(#{$rot}deg); 
	}
}

This will rotate the first link by 0 degrees, the second by 72, and so on. The problem is then getting the icons to move in the right direction after an action on the <nav> element. To do this, we can take advantage of the order of CSS transform operations: rotate happens first, followed by translate, meaning that if an element is rotated by 72 degrees, any horizontal translation will be along this new, re-oriented axis, so long as the amount of rotation remains unchanged.

For this example, we’ll say that the action takes place on a :hover over the <nav> element. The becomes:

@for $i from 1 through 6 {
	$rot: (($i - 2) * 72);
	a:nth-child(#{$i}) {
		transform: rotate(#{$rot}deg);
	}
	nav:hover a:nth-child(#{$i}) {
		transform: rotate(#{$rot}deg) translateX(7rem) rotate(-#{$rot}deg); 
	}
}

This will move the icons out in a circle from the center. Because the transforms are sequential, the second rotation straightens the icons.

Moving on Click

Trying to operate both the main <nav> and links at the same time with a hover state would be a difficult under any circumstances; it makes more sense to operate the main navigation on click, and the individual links on hover, by adding a script to the end of the page:

function clickSet() {
	circularnav.classList.toggle("clicked");
}
var circularnav = document.getElementsByClassName("ss-icon")[0];
circularnav.addEventListener("click", clickSet, false);

We can now place kind of action, like the transition of the links, on the presence or absence of the clicked state on the <nav> element, rather than a :hover event.

Adding Accessibility

While the links should be perfectly readable using text-to-speech, they are not keyboard navigable in any significant way, due to the fact that the links are hidden behind the <nav> element, and the latter cannot be focussed via the keyboard, at least by default. We can change that by providing the <nav> element with a tabindex value:

<nav tabindex="0">

…and adding a little JavaScript:

circularnav.addEventListener("keydown", function (e){
	if (e.keyCode === 13) { 
clickSet;
	}
});

This combination allows for the <nav> element to be reached via TAB, and activated by pressing the Enter key, producing the same results a click would.

Graceful Fallbacks & Improvements

A particularly nice aspect of the CSS used here is that browsers such as Internet Explorer 8 that do not support animations will also not support the method by which I’ve added the font, meaning that the result will be a simple collection of named links for each social media service. You would have to do something special for IE9, which exists in the twilight world before standards adherence.

While there’s much more to cover – I’m particularly pleased with the movement of the pseudo-element “hamburger” navigation bars, although returning them to position after click could be improved – this should be enough to get anyone started on the process of making their own radial navigation. For more information, take a look at the associated CodePen.

Enjoy this piece? I invite you to follow me at twitter.com/dudleystorey to learn more.
Check out the CodePen demo for this article at https://codepen.io/dudleystorey/pen/emVqYR