“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:
- HTML markup and JavaScript should be minimal;
- CSS animation should be used wherever possible;
- 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 SVG, I decided to use SymbolSet’s excellent “Social Circle” semantic 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 Sass 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