Menu Controls
Download Menu
Hint: You can rotate the menu by dragging it.

Using The Generated Menu

So you've picked your menu style and downloaded an all-new SVG menu. What then? Here is everything you need to know to customize your menu and embed it in your page.

Dissecting The Code

The code generated by the above generator is straightforward and—if you are familiar with SVG—fairly simple to customize. You almost don't need to do anything except add your own icons to the menu, add a label or icon to the menu's trigger—if you want, and then embed the menu directly into your page.

Before we get into adding your own icons, let's first get a quick overview of how the generated code is structured. The following snippet shows what a semi-circular menu of five items will look like in code:


<svg xmlns="https://www.w3.org/2000/svg" xmlns:xlink="https://www.w3.org/1999/xlink" viewBox="-2 -2 504 504" id="menu" style="transform-origin: 50% 50% 0px; transform: translate3d(0px, 0px, 0px); touch-action: none; -webkit-user-select: none;">
<style>
#menu {
    display: block;
    margin: 0 auto;
    /*overflow: visible;*/ /* uncomment this if you are using bouncing animations*/
}

a {
    cursor: pointer; /* SVG <a> elements don't get this by default, so you need to explicitly set it */
    outline: none;
}

/* You can change these default styles any way you want */

.item .sector {
    transition: all .1s linear;
    fill: #fff;
    stroke: #111;
}

.item:hover .sector, .item:focus .sector {
    fill: #eee;
}

.menu-trigger {
    fill: #EA2A55;
    pointer-events: auto; /* KEEP THIS to make sure it stays clickable even when SVG's pointer events is disabled */
}

.menu-trigger:hover, .menu-trigger:focus {
    cursor: pointer;
}
symbol {
    overflow: visible; /* KEEP THIS so that text will not get cut off it it is wider than the icon width */
}
</style>
<g id="symbolsContainer">
    <symbol class="icon icon-" id="icon-1" viewBox="0 0 40 40"><!--Replace the contents of this symbol with the content of your icon--><rect fill="none" stroke="#111" width="100%" height="100%"></rect><text fill="#222" x="50%" y="50%" dy=".3em" text-anchor="middle" font-size="1.2em">1</text></symbol>

    <symbol class="icon icon-" id="icon-2" viewBox="0 0 40 40"><!--Replace the contents of this symbol with the content of your icon--><rect fill="none" stroke="#111" width="100%" height="100%"></rect><text fill="#222" x="50%" y="50%" dy=".3em" text-anchor="middle" font-size="1.2em">2</text></symbol>

    <symbol class="icon icon-" id="icon-3" viewBox="0 0 40 40"><!--Replace the contents of this symbol with the content of your icon--><rect fill="none" stroke="#111" width="100%" height="100%"></rect><text fill="#222" x="50%" y="50%" dy=".3em" text-anchor="middle" font-size="1.2em">3</text></symbol>

    <symbol class="icon icon-" id="icon-4" viewBox="0 0 40 40"><!--Replace the contents of this symbol with the content of your icon--><rect fill="none" stroke="#111" width="100%" height="100%"></rect><text fill="#222" x="50%" y="50%" dy=".3em" text-anchor="middle" font-size="1.2em">4</text></symbol>

    <symbol class="icon icon-" id="icon-5" viewBox="0 0 40 40"><!--Replace the contents of this symbol with the content of your icon--><rect fill="none" stroke="#111" width="100%" height="100%"></rect><text fill="#222" x="50%" y="50%" dy=".3em" text-anchor="middle" font-size="1.2em">5</text></symbol>

</g>
<g id="itemsContainer">
    <a class="item" role="link" tabindex="0" target="_parent" id="item-1" xlink:href="" xlink:title="" transform="matrix(1,0,0,1,0,0)" data-svg-origin="250 250" style=""><path fill="none" stroke="#111" stroke-width="1" class="sector" d="M250,250 l250,0 A250,250 0 0,0 452.25424859373686,103.05368692688171 z"></path><use xlink:href="#icon-1" width="40" height="40" x="391.6795959472656" y="177.4671173095703" transform="rotate(72 411.6795959472656 197.4671173095703)"></use></a>
    <a class="item" role="link" tabindex="0" target="_parent" id="item-2" xlink:href="" xlink:title="" transform="matrix(0.80901,-0.58778,0.58778,0.80901,-99.20056166685515,194.69206447938143)" data-svg-origin="250 250" style=""><path fill="none" stroke="#111" stroke-width="1" class="sector" d="M250,250 l250,0 A250,250 0 0,0 452.25424859373686,103.05368692688171 z"></path><use xlink:href="#icon-2" width="40" height="40" x="391.6795959472656" y="177.4671173095703" transform="rotate(72 411.6795959472656 197.4671173095703)"></use></a>
    <a class="item" role="link" tabindex="0" target="_parent" id="item-3" xlink:href="" xlink:title="" transform="matrix(0.30901,-0.95105,0.95105,0.30901,-65.01837766752521,410.5098804800515)" data-svg-origin="250 250" style=""><path fill="none" stroke="#111" stroke-width="1" class="sector" d="M250,250 l250,0 A250,250 0 0,0 452.25424859373686,103.05368692688171 z"></path><use xlink:href="#icon-3" width="40" height="40" x="391.6795959472656" y="177.4671173095703" transform="rotate(72 411.6795959472656 197.4671173095703)"></use></a>
    <a class="item" role="link" tabindex="0" target="_parent" id="item-4" xlink:href="" xlink:title="" transform="matrix(-0.30901,-0.95105,0.95105,-0.30901,89.49011951994842,565.0183776675252)" data-svg-origin="250 250" style=""><path fill="none" stroke="#111" stroke-width="1" class="sector" d="M250,250 l250,0 A250,250 0 0,0 452.25424859373686,103.05368692688171 z"></path><use xlink:href="#icon-4" width="40" height="40" x="391.6795959472656" y="177.4671173095703" transform="rotate(72 411.6795959472656 197.4671173095703)"></use></a>
    <a class="item" role="link" tabindex="0" target="_parent" id="item-5" xlink:href="" xlink:title="" transform="matrix(-0.80901,-0.58778,0.58778,-0.80901,305.3079355206185,599.2005616668552)" data-svg-origin="250 250" style=""><path fill="none" stroke="#111" stroke-width="1" class="sector" d="M250,250 l250,0 A250,250 0 0,0 452.25424859373686,103.05368692688171 z"></path><use xlink:href="#icon-5" width="40" height="40" x="391.6795959472656" y="177.4671173095703" transform="rotate(72 411.6795959472656 197.4671173095703)"></use></a>
</g>
<g id="trigger" class="trigger menu-trigger" role="button">
    <circle cx="250" cy="250" r="30"></circle>
    <!-- menu button label or icon goes here -->
</g>
</svg>
                

Okay that looks like a lot. But if you take a closer look, you will see that there are a lot of repeated structures. Let's focus on those. I will skip the styles for now and get back to them in an upcoming section.

  1. The style element contains the default styles. I didn't add too many of those so that you don't have to override a lot. You will only find fill and stroke colors, as well as a couple of styles for the root svg#menu that we'll talk about in the next section.
  2. Right after the styles, we have a container with an ID of symbolsContainer:
    <g id="symbolsContainer">
        <!-- ... -->
    </g>
                            

    This is the group containing the <symbol> elements that are used to define your icons.

  3. The number of <symbol>s depends on the number of menu items you choose. Each symbol is used to wrap the contents of an icon. By default, the symbols contain a rect and a text element representing the number of the item—kind of like an index. For each symbol, you will remove both of these, and paste the contents of your icon.
    <symbol class="icon icon-" id="icon-1" viewBox="0 0 40 40">
        <!--Replace the contents of this symbol with the content of your icon-->
        <rect fill="none" stroke="#111" width="100%" height="100%"></rect>
        <text fill="#222" x="50%" y="50%" dy=".3em" text-anchor="middle" font-size="1.2em">1</text>
    </symbol>
                            

    Each symbol also gets a class name and an ID, which you are also free to change.

    If you are adding a “download” icon to an item similar to the one you can see in the download button above, then the contents of a symbol would look like this:

    <symbol class="icon icon-download" id="icon-1" viewBox="0 0 40 40"><
        <path d="M16 18l8-8h-6v-8h-4v8h-6zM23.273 14.727l-2.242 2.242 8.128 3.031-13.158 4.907-13.158-4.907 8.127-3.031-2.242-2.242-8.727 3.273v8l16 6 16-6v-8z"></path>
    </symbol>
                            

    Notice how I added the suffix download to the icon- prefix in the list of class names.

    The viewBox set is also a default value that you can change according to that of your icon's. If you have created or used SVG sprites before, then you know what this is for. If not, you can read more about it here.

  4. After the symbols used to define your icons, there is a container for the actual menu items made of paths and <use> elements referencing the icons defined in the <symbol> elements earlier. Each item is wrapped in an <a> element; I have added the xlink:href and xlink:title by default, so you will want to add URLs and titles for those.
    <a class="item" role="link" tabindex="0" target="_parent" id="item-1" xlink:href="" xlink:title="" transform="matrix(1,0,0,1,0,0)" data-svg-origin="250 250" style="">
        <path fill="none" stroke="#111" stroke-width="1" class="sector" d="M250,250 l250,0 A250,250 0 0,0 452.25424859373686,103.05368692688171 z"></path>
        <use xlink:href="#icon-1" width="40" height="40" x="391.6795959472656" y="177.4671173095703" transform="rotate(72 411.6795959472656 197.4671173095703)"></use>
    </a>
                            
    The transform attribute contains the transformation needed to rotate the item. The data-svg-origin attribute is only needed if you will use GSAP to animate the items—it makes animating the items much simpler. More about this in the Examples section.

    The <path> element represents the sector shape, and the <use> element is the only thing you might need to touch here: if you change the IDs on the symbols, then you'll need to change the ID references on the uses too.

    The <use> element is also where you specify the height and width of your icon. Remember: these dimensions will be used as a viewport for the viewbox specified in the referenced <symbol>.

  5. At the end of the generated code is a #trigger group which, as the name suggests, is the group containing the circle making up the trigger of the menu.
    <g id="trigger" class="trigger menu-trigger" role="button">
        <circle cx="250" cy="250" r="30"></circle>
        <!-- menu button label or icon goes here -->
    </g>
                            
    Inside this group, you can place an icon, a label, or whatever you want to be the trigger of the menu. Just make sure you position the icon or label at the center of the menu, which is located at 250px 250px across the SVG.

Adding Your Icons

As mentioned in the previous section, all you need to do is copy your icon's content into the <symbol>s and, if you want, add or change any class names and IDs.

After adding your icons, you reference each one in the <use> elements inside the items.

Most icons usually come in square sizes, so the viewBox and use dimensions are square as well by default.

If you do change the dimensions of the icon so that it no longer is a square, you may have to slightly tweak its position inside the sector to center it. Note that if you use an icon service like Icomoon, you don't have to do any tweaking; just place the icon content where it should and then specify the viewbox and icon size as it comes by default. Or don't!

The following is an example where I just generated a semi-circular menu with four items, downloaded the icons from Icomoon, pasted the content of each icon’s <svg> (minus the actual <svg> tag) into the corresponding <symbol> and... well, that's all! In many cases, that's really all you will have to do for your icons to be placed in the menu.

I did not add any hover styles or effects, though, so that the sole focus of this example is adding the icons. But I did add a piece of text to the trigger and positioned it so that it is centered in the menu.

Example 1.

And the code for the above example is:

<svg xmlns="https://www.w3.org/2000/svg" xmlns:xlink="https://www.w3.org/1999/xlink" viewBox="-2 -2 504 504" id="menu" style="transform-origin: 50% 50% 0px; transform: translate3d(0px, 0px, 0px); touch-action: none; -webkit-user-select: none;">
<style>
#menu {
    display: block;
    margin: 0 auto;
    /*overflow: visible;*/ /* uncomment this if you are using bouncing animations*/
}

a {
    cursor: pointer; /* SVG <a> elements don't get this by default, so you need to explicitly set it */
    outline: none;
}

.item .sector {
    transition: all .1s linear;
    fill: #fff;
    stroke: #111;
}

/* You can change these default styles any way you want */

.item:hover .sector, .item:focus .sector {
    fill: #eee;
}

.menu-trigger {
    fill: #EA2A55;
    pointer-events: auto; /* KEEP THIS to make sure it stays clickable even when SVG's pointer events is disabled */
}

.menu-trigger:hover, .menu-trigger:focus {
    cursor: pointer;
}
symbol {
    overflow: visible; /* KEEP THIS so that text will not get cut off it it is wider than the icon width */
}
</style>
    <g id="symbolsContainer">
        <symbol class="icon icon-" id="icon-1" viewBox="0 0 40 40">
            <path d="M29.555 11.501l-14-9.333c-0.336-0.224-0.774-0.224-1.109 0l-14 9.333c-0.278 0.185-0.445 0.498-0.445 0.832v9.333c0 0.334 0.167 0.647 0.445 0.832l14 9.333c0.168 0.112 0.361 0.168 0.555 0.168s0.387-0.056 0.555-0.168l14-9.333c0.278-0.185 0.445-0.498 0.445-0.832v-9.333c0-0.334-0.167-0.647-0.445-0.832zM15 20.465l-5.197-3.465 5.197-3.465 5.197 3.465-5.197 3.465zM16 11.798v-6.93l11.197 7.465-5.197 3.465-6-4zM14 11.798l-6 4-5.197-3.465 11.197-7.465v6.93zM6.197 17l-4.197 2.798v-5.596l4.197 2.798zM8 18.202l6 4v6.93l-11.197-7.465 5.197-3.465zM16 22.202l6-4 5.197 3.465-11.197 7.465v-6.93zM23.803 17l4.197-2.798v5.596l-4.197-2.798z" fill="#444444"></path>
        </symbol>

        <symbol class="icon icon-" id="icon-2" viewBox="0 0 40 40">
            <path d="M23.927 7.073c-0.215 0.306-2.015 2.726-6.267 4.32-1.96-3.6-4.131-6.558-4.462-7 0.899-0.217 1.837-0.332 2.802-0.332 3.038 0 5.815 1.14 7.927 3.013zM16.836 16.123c-6.167 1.994-9.449 7.427-9.72 7.891-1.91-2.12-3.074-4.924-3.074-7.996 0-0.122 0.002-0.244 0.006-0.366 0.523 0.011 6.318 0.085 12.29-1.702 0.342 0.67 0.669 1.35 0.97 2.029-0.158 0.045-0.315 0.092-0.471 0.142zM16 0c-8.837 0-16 7.163-16 16s7.163 16 16 16 16-7.163 16-16-7.163-16-16-16zM16 30c-7.72 0-14-6.28-14-14s6.28-14 14-14c7.72 0 14 6.28 14 14s-6.28 14-14 14zM19.372 14.903c-0.248-0.583-0.515-1.162-0.793-1.732 4.421-1.805 6.425-4.374 6.677-4.715 1.665 2.035 2.674 4.627 2.7 7.449-0.394-0.083-4.348-0.885-8.327-0.385-0.084-0.205-0.17-0.41-0.258-0.617zM15.35 12.112c-5.594 1.485-10.52 1.464-11.055 1.457 0.775-3.711 3.276-6.795 6.622-8.373 0.315 0.432 2.452 3.394 4.434 6.916zM18.041 17.984c0.026-0.009 0.052-0.018 0.079-0.027 1.679 4.363 2.373 8.019 2.551 9.068-1.436 0.611-3.015 0.95-4.671 0.95-2.764 0-5.313-0.943-7.34-2.524 0.215-0.44 2.636-5.117 9.381-7.468zM20.358 17.405c3.747-0.6 7.040 0.382 7.449 0.511-0.533 3.327-2.443 6.201-5.126 8.015-0.123-0.727-0.759-4.228-2.323-8.526z" fill="#444444"></path>
        </symbol>

        <symbol class="icon icon-" id="icon-3" viewBox="0 0 40 40">
            <path d="M32 6.076c-1.177 0.522-2.443 0.875-3.771 1.034 1.355-0.813 2.396-2.099 2.887-3.632-1.269 0.752-2.674 1.299-4.169 1.593-1.198-1.276-2.904-2.073-4.792-2.073-3.626 0-6.565 2.939-6.565 6.565 0 0.515 0.058 1.016 0.17 1.496-5.456-0.274-10.294-2.888-13.532-6.86-0.565 0.97-0.889 2.097-0.889 3.301 0 2.278 1.159 4.287 2.921 5.465-1.076-0.034-2.088-0.329-2.974-0.821-0.001 0.027-0.001 0.055-0.001 0.083 0 3.181 2.263 5.834 5.266 6.437-0.551 0.15-1.131 0.23-1.73 0.23-0.423 0-0.834-0.041-1.235-0.118 0.835 2.608 3.26 4.506 6.133 4.559-2.247 1.761-5.078 2.81-8.154 2.81-0.53 0-1.052-0.031-1.566-0.092 2.905 1.863 6.356 2.95 10.064 2.95 12.076 0 18.679-10.004 18.679-18.68 0-0.285-0.006-0.568-0.019-0.849 1.283-0.926 2.396-2.082 3.276-3.398z" fill="#444444"></path>
        </symbol>

        <symbol class="icon icon-" id="icon-4" viewBox="0 0 40 40">
            <path d="M26.688 0h-21.375c-2.922 0-5.313 2.391-5.313 5.313v21.375c0 2.922 2.391 5.313 5.313 5.313h21.375c2.922 0 5.313-2.391 5.313-5.313v-21.375c0-2.922-2.391-5.313-5.313-5.313zM10.244 14h11.513c0.218 0.627 0.337 1.3 0.337 2 0 3.36-2.734 6.094-6.094 6.094s-6.094-2.734-6.094-6.094c0-0.7 0.119-1.373 0.338-2zM28 14.002v11.998c0 1.1-0.9 2-2 2h-20c-1.1 0-2-0.9-2-2v-12h3.128c-0.145 0.644-0.222 1.313-0.222 2 0 5.014 4.079 9.094 9.094 9.094s9.094-4.079 9.094-9.094c0-0.687-0.077-1.356-0.222-2l3.128 0.002zM28 7c0 0.55-0.45 1-1 1h-2c-0.55 0-1-0.45-1-1v-2c0-0.55 0.45-1 1-1h2c0.55 0 1 0.45 1 1v2z" fill="#444444"></path>
        </symbol>

    </g>
    <g id="itemsContainer">
        <a class="item" role="link" tabindex="0" target="_parent" id="item-1" xlink:href="" xlink:title="" transform="matrix(1,0,0,1,0,0)" data-svg-origin="250 250" style="">
            <path fill="none" stroke="#111" stroke-width="1" class="sector" d="M250,250 l250,0 A250,250 0 0,0 426.7766952966369,73.22330470336314 z"></path>
            <use xlink:href="#icon-1" width="40" height="40" x="387.05950927734375" y="164.94381713867188" transform="rotate(67.5 407.05950927734375 184.94381713867188)"></use>
        </a>
        <a class="item" role="link" tabindex="0" target="_parent" id="item-2" xlink:href="" xlink:title="" transform="matrix(0.7071,-0.7071,0.7071,0.7071,-103.55339059327378,249.99999999999997)" data-svg-origin="250 250" style="">
            <path fill="none" stroke="#111" stroke-width="1" class="sector" d="M250,250 l250,0 A250,250 0 0,0 426.7766952966369,73.22330470336314 z"></path>
            <use xlink:href="#icon-2" width="40" height="40" x="387.05950927734375" y="164.94381713867188" transform="rotate(67.5 407.05950927734375 184.94381713867188)"></use>
        </a>
        <a class="item" role="link" tabindex="0" target="_parent" id="item-3" xlink:href="" xlink:title="" transform="matrix(0,-1,1,0,0,500)" data-svg-origin="250 250" style="">
            <path fill="none" stroke="#111" stroke-width="1" class="sector" d="M250,250 l250,0 A250,250 0 0,0 426.7766952966369,73.22330470336314 z"></path>
            <use xlink:href="#icon-3" width="40" height="40" x="387.05950927734375" y="164.94381713867188" transform="rotate(67.5 407.05950927734375 184.94381713867188)"></use>
        </a>
        <a class="item" role="link" tabindex="0" target="_parent" id="item-4" xlink:href="" xlink:title="" transform="matrix(-0.7071,-0.7071,0.7071,-0.7071,249.99999999999997,603.5533905932738)" data-svg-origin="250 250" style="">
            <path fill="none" stroke="#111" stroke-width="1" class="sector" d="M250,250 l250,0 A250,250 0 0,0 426.7766952966369,73.22330470336314 z"></path>
            <use xlink:href="#icon-4" width="40" height="40" x="387.05950927734375" y="164.94381713867188" transform="rotate(67.5 407.05950927734375 184.94381713867188)"></use>
        </a>
    </g>
    <g id="trigger" class="trigger menu-trigger" role="button">
        <circle cx="250" cy="250" r="30"></circle>
        <text text-anchor="middle" x="250" y="254" fill="#fff" font-size=".7em">Follow</text>
    </g>
</svg>

                

As you have noticed, the first thing that grabs one's attention is the extra space inside the SVG, below the menu. The extra space is due to the fact that the SVG canvas (defined by the viewBox) is 500 pixels wide and 500 pixels tall, while the menu is itself is only 250 pixels tall. To get rid of this white space, change the viewBox value to -2 -2 504 252 to avoid that. (All we did is make the SVG canvas 250px in height to "cut off" that extra white space.) For more information about how the viewBox attribute works refer to this article.

And here is the same example with the cropped white space. Unless we don't crop the trigger with the rest of the SVG, we need to adjust the position of the text, which is what I did in the second example here:

Example 2.


Adding Text Labels Instead Of Icons

If you want to add a text label instead of an icon, you can also do that by simply replacing the indices in the <symbol>s by your own labels. An example of this is the menu fixed at the bottom of the viewport. Replacing the content of the <text> elements was enough to achieve that.

Hint: The bigger the icon size you choose, the bigger the words will appear. Think of the square inside each item as the containing box of the word inside it. The wider the box, the bigger the word will appear.

In some cases, the label might be slightly wider than the <symbol> it's contained in. To avoid the text being cut off by the boundaries of the symbol, I've set overflow: visible; on the latter.

Styling The Menu

First things first: if you haven't rotated the menu while creating it, remove this part from the root <svg>:

style="transform-origin: 50% 50% 0px; transform: translate3d(0px, 0px, 0px); touch-action: none; -webkit-user-select: none;"
                

You don't need it. Unless there is a non-zero transformation applied, just get rid of it.

I've removed all and any inline styles in the menu, except the transformations which will come in handy if/when you want to animate the items with CSS.

You can select and style the SVG elements inside the menu just like you would do with HTML. Apply colors and transitions to your liking.

Note that overflow is set to visible on the <svg> so that the items don't get cut off when they are animated outside the bounds of the SVG viewport—for example, when using bouncing easing functions.

Remember that you can also style the menu items further using SVG as well. For example, you can apply drop shadows to them; this would be particularly useful if the menu is going to be fixed and will overlap other content. You can learn more about applying drop shadows here and here.

Another great addition is rounding the corners of the SVG and/or its container (if you are using the padding hack). I've left this out for your to decide and add should you want to.

Embedding The Menu

The menu needs to be embedded inline in the page because of how it is structured. Embedding it as an image will not allow you to interact with it, so you can embed it either inline or using <object> or <iframe>. The latter two are possible, but you would need to completely separate the menu trigger from the SVG and position them independently from each other so that you can enable and disable pointer events on the SVG (<object> or <iframe>) while maintaining them on the trigger. I recommend embedding it inline.

To embed it inline is simple and it will work out of the box. However, you will need to use the padding hack to make the SVG fluid in IE, otherwise it will have a fixed 150px height.

To apply the padding hack you will need to wrap the SVG in a container and apply some styles to both the SVG and the container as follows:

<div class="menu-wrapper">
    <svg xmlns="https://www.w3.org/2000/svg" xmlns:xlink="https://www.w3.org/1999/xlink" viewBox="-2 -2 504 504" id="menu">
        <!-- ... -->
    </svg>
</div>
            

Remember to set the viewBox to -2 -2 504 252 if you generated a semi-circular menu. You don't need to specify any height or width unless you are creating a fixed-size menu.

Now the styles for the padding hack are:

.menu-wrapper {
    position: relative;
    height: 0;
    width: 100%; /* any width you want */
    padding-top: 100%; /* if the menu is in full circle mode. 50% if it is in semi-circle mode. */
}
#menu {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
}
            

This will make the menu fluid. If you want to limit its width without having to need media queries, you can wrap it in yet another container and set max-width on that container.

And that's pretty much just it.

Browser Support

SVG is supported in IE9+ and all other modern browsers. For IE8-, a non-circular non-SVG fallback can be provided by detecting SVG support using JS or just using conditional comments.

Note that the transformations that are applied to the menu by default are using an SVG transform attribute for maximum browser support. IE does not support CSS transforms applied to SVG elements. This is also why I recommend using JavaScript for animating th menu. You can use snap.svg which can animate SVG attributes with ease, or GSAP which will apply CSS animations to the SVG elements but also detects support and provides fallback to make the animations also work in IE. See the Examples section below for live examples.

Also note that the <svg> itself is rotated using CSS transforms (when you rotate it by dragging), and the rotation is applied sans prefixes, so remember to add the necessary prefixes to make sure it stays rotated in all browsers.

Animating The Menu

For opening and closing animation effects, I recommend using JavaScript. The following live examples use snap.svg (first one) and GSAP (second and third) to create different opening and closing effects. You can simply copy the JavaScript from these demos directly into your page and have it work out of the box with the generated menu code. All you need to do is remember to add the library that you are using to your page, before the script.

Moreover, the demos also include the logic required to close the menu when anywhere outside it is clicked.

Finally, the demos also enable and disable pointer events on the SVG when it is open or closed. This is where the pointer-events: auto; that is set on the #trigger by default comes in use: when the menu is clicked and the pointer events on the SVG are disabled (to prevent it from blocking the content behind it), you need to make sure the trigger responds to pointer events, so make sure you keep this rule all the time.

Example #1

In this example, I am using snap.svg to animate the menu's opening and closing. Instead of animating every item on its own, I am animating the entire group that contains the items, and applying a bouncing effect upon opening and closing it.

Click on the menu's trigger to close/open it.

You can get the JavaScript for this effect, paste it into your SVG file, link to snap.svg right before the script, and then embed it in your page.

You can find the code for this example here.

The example also includes a label in the trigger which changes upon being clicked. Feel free to customize the menu as much as you need.

Example #2

This example uses GSAP for the animation. Click on the menu trigger to open and close it.

In this example, the items are animated one by one, instead of all together. This animation effect is courtesy of @greensock.

You can get the code for this animation here.

Remember to include the latest version of TweenMax (v1.16.0)—it will not work with previous versions. You can find the link to the latest one (on a CDN) in the JS settings panel in the Codepen.

Example #3

The third example also uses TweenMax (v1.16.0) but creates a different kind of effect: one of an opening and closing fan.

Once more, you can copy-paste the code from the example into your page and use it as it, changing the icons, labels, etc. to your liking.

You can find the code for this example here.

In most cases, you'll want to start with a closed demo, so make sure you switch the initial content of the trigger accordingly, and set the open flag in JavaScript to false.

Again, tou can find the link to the latest TweenMax in the JS settings panel in the Codepen.

Why Not CSS?

Because SVG is better; it is better equipped and more flexible for creating and animating non-rectangular elements. I have written about building circular navigations like these with CSS, but the CSS technique used in CSS feels a bit hacky, not to mention buggy in some browsers. This gives SVG an advantage over CSS for creating this kind of UI elements, especially considering that browser support is pretty much the same for both, minus the buggy CSS behavior.

An additional benefit to using SVG is that the very nature of elements like paths in SVG gives us more flexibility to create really impressive animations that would not be possible in CSS otherwise. Plus, with SVG, you don't need media queries to make the menu responsive—it will scale as you'd expect it to, unlike the CSS version which does require media queries.

Moreover, adding images and content to the SVG version is also much, much simpler than doing the same thing in the CSS. As a matter of fact, adding custom images to the CSS circular menu is simply not possible in some cases, and the text rendering isn't the best in some browsers. Thus, again, SVG is superior for creating this kind UI element.

A follow-up post will be published on my blog explaining how the menu is created using SVG.

About Pie Menus: Design and UX Considerations

Don Hopkins has conducted a lot of research on pie menus from a design and user experience perspective. His many articles can be found on his website, where he wrote a lot about pie menus, including an empirical comparison of linear versus pie menus.

Some of the important points Hopkins made include:

Unlike rectangular menus, PieMenus place menu items in a circle around the clicked point. Therefore the user moves the mouse at different angles to reach different options, rather than moving it different distances. Angles are easier than distances to hold in motor memory than distances, and the user of the pie menu does not need to move the mouse such accurate distances. These factors make pie menus faster to use than rectangular menus.

Pie menus work well with up to about 8 options. After that, the menus need to be nested. This is not a great disadvantage: nested pie menus are far faster to use than nested rectangular menus. (However, some studies have indicated that the optimal number of "slices" is four, since the operator can then have huge errors in movement and still accurately navigate the menus.

Wikipedia suggests that the optimal number of items per menu is between three and 12—which is why you can find this range in the above demo. I found that up to 12 items in full circle mode was still acceptable without sacrificing pointer events/space. Up to six items in semi-circle mode was the maximum number while maintaining comfortable gestures and pointer events. I recommend not exceeding that number, otherwise you'd be counteracting the benefits of using a pie menu.

Also note that in some cases, you may be better off using a linear menu. For example, dynamic and large menus that also need submenus might be better off being linear.

You should also keep in mind that pie menus take up more screen estate when they are open, which is another reason why big menus should probably not be created circular.

Should you decide to go with a circular menu, I hope you find this tool useful.