← Back to all tutorials

How to create a circular menu with Tailwind CSS and JavaScript

#
Published and written on Oct 09 2024 by Michael Andreuzza

Hello everyone, today we are going to create a circular menu with Tailwind CSS and JavaScript.

What is a Circular Menu?

A circular menu is a user interface element where menu items are arranged in a circular or radial layout, emanating from a central button or point. This type of menu is often used to save space and create a visually appealing, interactive navigation experience. When triggered, the menu items fan out in a circle or semi-circle around the central button, offering users quick access to various options.

Use Cases

Circular menus are particularly useful in the following scenarios:

  1. Mobile apps: Circular menus are often used in mobile applications to provide quick access to actions or tools without taking up too much screen space.
  2. Media or design apps: Circular menus allow users to select tools, colors, or actions in an intuitive, visually engaging way.
  3. Interactive dashboards: In dashboards or control panels, circular menus can be used to present grouped actions in a space-efficient manner.
  4. Gaming interfaces: Games often use circular menus to display options, tools, or weapons around a central point for quick selection.
  5. Navigation menus: For more creative or artistic websites, circular menus can be used to stand out and enhance the user experience.

Now, let’s write the markup

The wrapper

ID’s

  • id="menuContainer": This line of code will define the id of the wrapper. This id will be used to target the wrapper in the JavaScript code. Classes
  • relative: This class will make the wrapper element relative to its position on the page.
<div
    class="relative"
    id="menuContainer">
  <!-- Menu items goe here -->
</div>

The button

ID’s

  • id="menuToggle": This line of code will define the id of the button. This id will be used to target the button in the JavaScript code. Classes
  • absolute: This class will make the button element absolutely positioned.
  • top-1/2: This class will position the button at the top of the screen, centered horizontally.
  • left-1/2: This class will position the button at the left of the screen, centered vertically.
  • transform -translate-x-1/2 -translate-y-1/2: This class will translate the button by half its width and half its height.
<button
     id="menuToggle"
     class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
  Menu
</button>

ID’s

  • id="circularMenu": This line of code will define the id of the link wrapper. Classes
  • absolute: This class will make the link wrapper element absolutely positioned.
  • top-0: This class will make the link wrapper element start at the top of the page.
  • left-0: This class will make the link wrapper element start at the left of the page.
  • w-full: This class will make the link wrapper element take up the full width of the page.
  • h-full: This class will make the link wrapper element take up the full height of the page.
  • You can also use size-full instead of w-full and h-full if you prefer.

Classes

  • menu-item: This is a custom class that will be used to grab them with the querySelectorAll method.
  • absolute: This class will make the link items absolutely positioned.
  • opacity-0: This class will make the link items start with an opacity of 0.
  • invisible: This class will make the link items start with an opacity of 0 and also make them invisible.
<nav
   id="circularMenu"
   class="absolute top-0 left-0 w-full h-full">
  <a href="#" class="menu-item absolute opacity-0 invisible ">1</a>
  <a href="#" class="menu-item absolute opacity-0 invisible ">2</a>
  <a href="#" class="menu-item absolute opacity-0 invisible ">3</a>
</nav>

The full markup

This is the full markup code for the circular menu with Tailwind CSS and JavaScript.Irrelevant classes have been omitted for brevity.

<div class="relative" id="menuContainer">
  <button id="menuToggle" class="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2">
   Menu
  </button>
  <nav id="circularMenu" class="absolute top-0 left-0 w-full h-full">
    <a href="#" class="menu-item absolute opacity-0 invisible ">1</a>
    <a href="#" class="menu-item absolute opacity-0 invisible ">2</a>
    <a href="#" class="menu-item absolute opacity-0 invisible ">3</a>
  </nav>
</div>

The JavaScript code

The variables

  • const menuToggle = document.getElementById("menuToggle");: This line of code will get the button element with the id of “menuToggle” and store it in a variable called menuToggle.
  • const menuItems = document.querySelectorAll(".menu-item");: This line of code will get all the link items with the class of “menu-item” and store them in a variable called menuItems.
  • let isOpen = false;: This line of code will define a variable called isOpen and set its value to false.
const menuToggle = document.getElementById("menuToggle");
const menuItems = document.querySelectorAll(".menu-item");
let isOpen = false;

The event listeners

  • menuToggle.addEventListener("click", toggleMenu);: This line of code will add an event listener to the button element with the id of “menuToggle” that will call the toggleMenu function when the button is clicked.
menuToggle.addEventListener("click", toggleMenu);

The toggleMenu function

The toggleMenu function will be used to toggle the visibility of the menu items.

  • function toggleMenu() {: This line of code will define a function called toggleMenu that will be called when the button is clicked.
  • isOpen = !isOpen;: This line of code will toggle the value of isOpen from false to true and vice versa.
  • menuItems.forEach((item, index) => {: This line of code will iterate over each link item in the menuItems array and pass the link item and its index to the anonymous function.
  • const delay = index * 50;: This line of code will define a variable called delay and set its value to index * 50.
  • setTimeout(() => {: This line of code will use the setTimeout function to delay the execution of the following code by delay milliseconds.
  • if (isOpen) {: This line of code will check if isOpen is true.
  • const angle = (index / menuItems.length) * 2 * Math.PI;: This line of code will define a variable called angle and set its value to (index / menuItems.length) * 2 * Math.PI.
  • const radius = 50;: This line of code will define a variable called radius and set its value to 50.
  • const x = Math.cos(angle) * radius + 50;: This line of code will define a variable called x and set its value to Math.cos(angle) * radius + 50.
  • const y = Math.sin(angle) * radius + 50;: This line of code will define a variable called y and set its value to Math.sin(angle) * radius + 50.
  • item.style.left = ${x}%;: This line of code will set the left style of the link item to x percent.
  • item.style.top = ${y}%;: This line of code will set the top style of the link item to y percent.
  • item.classList.remove("opacity-0", "invisible");: This line of code will remove the opacity-0 and invisible classes from the link item.
  • item.style.transform = translate(-50%, -50%) scale(1);: This line of code will set the transform style of the link item to translate(-50%, -50%) scale(1).
  • } else {: This line of code will close the menu if isOpen is false.
  • item.style.left = "50%";: This line of code will set the left style of the link item to 50%.
  • item.style.top = "50%";: This line of code will set the top style of the link item to 50%.
  • item.classList.add("opacity-0", "invisible");: This line of code will add the opacity-0 and invisible classes to the link item.
  • item.style.transform = translate(-50%, -50%) scale(0.5);: This line of code will set the transform style of the link item to translate(-50%, -50%) scale(0.5).
  • }, delay);: This line of code will close the menu if isOpen is false.
  • menuToggle.textContent = isOpen ? "Close" : "Menu";: This line of code will toggle the text of the button if isOpen is true or false.
function toggleMenu() {
    isOpen = !isOpen;
    menuItems.forEach((item, index) => {
        const delay = index * 50; // Stagger the animation
        setTimeout(() => {
            if (isOpen) {
                const angle = (index / menuItems.length) * 2 * Math.PI;
                const radius = 50; // Reduced radius to bring items closer to the button
                const x = Math.cos(angle) * radius + 50;
                const y = Math.sin(angle) * radius + 50;
                item.style.left = `${x}%`;
                item.style.top = `${y}%`;
                item.classList.remove("opacity-0", "invisible");
                item.style.transform = `translate(-50%, -50%) scale(1)`;
            } else {
                item.style.left = "50%";
                item.style.top = "50%";
                item.classList.add("opacity-0", "invisible");
                item.style.transform = `translate(-50%, -50%) scale(0.5)`;
            }
        }, delay);
    });

    // Toggle the menu button text
    menuToggle.textContent = isOpen ? "Close" : "Menu";
}
const menuToggle = document.getElementById("menuToggle");
const menuItems = document.querySelectorAll(".menu-item");
let isOpen = false;

menuToggle.addEventListener("click", toggleMenu);

function toggleMenu() {
    isOpen = !isOpen;
    menuItems.forEach((item, index) => {
        const delay = index * 50; // Stagger the animation
        setTimeout(() => {
            if (isOpen) {
                const angle = (index / menuItems.length) * 2 * Math.PI;
                const radius = 50; // Reduced radius to bring items closer to the button
                const x = Math.cos(angle) * radius + 50;
                const y = Math.sin(angle) * radius + 50;
                item.style.left = `${x}%`;
                item.style.top = `${y}%`;
                item.classList.remove("opacity-0", "invisible");
                item.style.transform = `translate(-50%, -50%) scale(1)`;
            } else {
                item.style.left = "50%";
                item.style.top = "50%";
                item.classList.add("opacity-0", "invisible");
                item.style.transform = `translate(-50%, -50%) scale(0.5)`;
            }
        }, delay);
    });

    // Toggle the menu button text
    menuToggle.textContent = isOpen ? "Close" : "Menu";
}

Conclusion

This is a simple circular menu that you can recreate with Tailwind CSS and JavaScript. You can customize the colors, number of menu items, and radius to your liking.

Hope you enjoyed this tutorial and have a good day!

/Michael Andreuzza

Did you like this tutorial? Please share it with your friends!

Reviews and opinions

Get lifetime access to every theme available today for $199 and own them forever. Plus, new themes, lifetime updates, use on unlimited projects and enjoy lifetime support.

No subscription required!

Lexington

Beautifully designed HTML, Astro.js and Tailwind themes! Save months of time and build your startup landing page in minutes.