Dropdown Menu using HTML, CSS & JavaScript

5 min read
Animated Dropdown Menu Demo

Project Overview

This project demonstrates how to create a highly interactive and visually appealing dropdown menu using HTML, CSS, and JavaScript. The dropdown is not just functional but also features smooth animations and a unique floating icon effect that follows the cursor, providing a delightful user experience.

The menu items are dynamically generated using JavaScript, making it easy to add, remove, or change the options. When an item is selected, the main button updates to reflect the choice, both in text and with a corresponding icon. The entire component is styled with a modern, dark theme and utilizes CSS custom properties for easy customization of colors, animations, and dimensions.

HTML Structure

Let's start with the basic HTML for the Dropdown Menu.


<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Dropdown Menu | Coders_Section</title>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width" />
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <div class="dropdown-container">
      <button class="dropdown-button main-button">
        <span class="dropdown-title-icon">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
            <path d="M16 8.049c0-4.446-3.582-8.05-8-8.05C3.58 0-.002 3.603-.002 8.05c0 4.017 2.926 7.347 6.75 7.951v-5.625h-2.03V8.05H6.75V6.275c0-2.017 1.195-3.131 3.022-3.131.876 0 1.791.157 1.791.157v1.98h-1.009c-.993 0-1.303.621-1.303 1.258v1.51h2.218l-.354 2.326H9.25V16c3.824-.604 6.75-3.934 6.75-7.951z" />
          </svg>
        </span>
        <span class="dropdown-title text-truncate">Facebook</span>
        <span class="dropdown-arrow">
          <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
            <path d="M7.247 11.14 2.451 5.658C1.885 5.013 2.345 4 3.204 4h9.592a1 1 0 0 1 .753 1.659l-4.796 5.48a1 1 0 0 1-1.506 0z" />
          </svg>
        </span>
      </button>
      <div class="dropdown-list-container">
        <div class="dropdown-list-wrapper">
          <ul class="dropdown-list"></ul>
          <div class="floating-icon" aria-hidden="true"></div>
        </div>
      </div>
    </div>
    <script src="script.js"></script>
  </body>
</html>
                        

CSS Styling

Here's the CSS that creates the animated dropdown and floating icon effect.


@import url("https://fonts.googleapis.com/css2?family=Roboto&display=swap");

*,
*::before,
*::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

:root {
  --bg-color: #000000;
  --primary-bg-color: #333740;
  --primary-color: #00fbff;
  --text-color: #b1b8ca;
  --text-active: #ffffff;
  --button-hover-bg-color: #2b2e34;
  --border-color: #494d59;
  --dropdown-height: 0;
  --rotate-arrow: 0;
  --translate-value: 0;
  --list-opacity: 0;
  --transition-time: 0.4s;
  --transition-timing: cubic-bezier(0.25, 0.46, 0.45, 0.94);
  --border-radius: 1.4rem;
  --list-button-height: 4.6rem;
  --floating-icon-size: 26;
  --floating-icon-top: 0;
  --floating-icon-left: 0;
}

html {
  font-size: 62.5%;
}

html,
body {
  height: 100%;
}

body {
  display: flex;
  flex-direction: column;
  align-items: center;
  background-color: var(--bg-color);
  padding: 1.5rem;
  line-height: 1.4;
}

button {
  border: none;
  cursor: pointer;
  background-color: transparent;
  outline: none;
}

svg {
  height: 1.6rem;
  width: 1.6rem;
}

.text-truncate {
  text-overflow: ellipsis;
  overflow: hidden;
  white-space: nowrap;
}

.dropdown-container {
  margin-top: 30vh;
  display: flex;
  flex-direction: column;
  width: 100%;
  max-width: 34rem;
}

.dropdown-title-icon,
.dropdown-arrow {
  display: inline-flex;
}

.dropdown-title {
  margin: 0 auto 0 1.8rem;
  text-transform: capitalize;
}

.dropdown-button {
  font-family: "Roboto", sans-serif;
  font-weight: 400;
  font-size: 1.7rem;
  display: flex;
  align-items: center;
  padding: 0 1.8rem;
}

.dropdown-button svg {
  transition: all var(--transition-time) var(--transition-timing);
  fill: var(--text-color);
}

.dropdown-button svg,
.dropdown-button span {
  pointer-events: none;
}

.dropdown-button:hover,
.dropdown-button:focus {
  color: var(--text-active);
}

.dropdown-button:hover svg,
.dropdown-button:focus svg {
  fill: var(--text-active);
}

.main-button {
  height: 5.2rem;
  border-radius: var(--border-radius);
  color: var(--text-color);
  background-color: var(--primary-bg-color);
  border: 0.1rem solid var(--border-color);
  transition: all var(--transition-time) var(--transition-timing);
}

.main-button:focus {
  border: 0.1rem solid var(--primary-color);
  box-shadow: 0 0 0 0.2rem rgba(44, 98, 246, 0.4);
}

.main-button .dropdown-arrow {
  transition: transform var(--transition-time) var(--transition-timing);
  transform: rotate(var(--rotate-arrow));
  margin-left: 1.8rem;
}

.list-button {
  height: var(--list-button-height);
  transition: color var(--transition-time) var(--transition-timing);
  color: var(--text-color);
  overflow: hidden;
  cursor: none;
}

.dropdown-list-container {
  overflow: hidden;
  max-height: var(--dropdown-height);
  transition: max-height var(--transition-time) var(--transition-timing);
}

.dropdown-list-wrapper {
  margin-top: 1rem;
  padding: 1rem;
  background-color: var(--primary-bg-color);
  border-radius: var(--border-radius);
  border: 0.1rem solid var(--border-color);
  position: relative;
}

ul.dropdown-list {
  position: relative;
  list-style-type: none;
}

ul.dropdown-list::before {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  left: 0;
  z-index: 0;
  opacity: 0;
  height: var(--list-button-height);
  background-color: var(--button-hover-bg-color);
  transition: all var(--transition-time) linear;
  transform: translateY(var(--translate-value));
  border-radius: var(--border-radius);
  pointer-events: none;
}
ul.dropdown-list:hover::before,
ul.dropdown-list:hover ~ .floating-icon {
  opacity: 1;
}

li.dropdown-list-item {
  display: flex;
  flex-direction: column;
  position: relative;
  z-index: 1;
  opacity: var(--list-opacity);
  transition: opacity 0.8s var(--transition-timing);
}

.floating-icon {
  height: calc(var(--floating-icon-size) * 1px);
  width: calc(var(--floating-icon-size) * 1px);
  position: absolute;
  top: var(--floating-icon-top);
  left: var(--floating-icon-left);
  background-color: var(--border-color);
  border-radius: 1rem;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--transition-time) var(--transition-timing);
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.floating-icon svg {
  fill: var(--text-active);
}
                        

JavaScript Logic

The JavaScript handles the dynamic creation of list items and the interactive animations.


const root = document.documentElement;
const dropdownTitleIcon = document.querySelector(".dropdown-title-icon");
const dropdownTitle = document.querySelector(".dropdown-title");
const dropdownList = document.querySelector(".dropdown-list");
const mainButton = document.querySelector(".main-button");
const floatingIcon = document.querySelector(".floating-icon");

const icons = {
  linkedin: "M0 1.146C0 .513.526 0 1.175 0h13.65C15.474 0 16 .513 16 1.146v13.708c0 .633-.526 1.146-1.175 1.146H1.175C.526 16 0 15.487 0 14.854V1.146zm4.943 12.248V6.169H2.542v7.225h2.401zm-1.2-8.212c.837 0 1.358-.554 1.358-1.248-.015-.709-.52-1.248-1.342-1.248-.822 0-1.359.54-1.359 1.248 0 .694.521 1.248 1.327 1.248h.016zm4.908 8.212V9.359c0-.216.016-.432.08-.586.173-.431.568-.878 1.232-.878.869 0 1.216.662 1.216 1.634v3.865h2.401V9.25c0-2.22-1.184-3.252-2.764-3.252-1.274 0-1.845.7-2.165 1.193v.025h-.016a5.54 5.54 0 0 1 .016-.025V6.169h-2.4c.03.678 0 7.225 0 7.225h2.4z",
  instagram: "M8 0C5.829 0 5.556.01 4.703.048 3.85.088 3.269.222 2.76.42a3.917 3.917 0 0 0-1.417.923A3.927 3.927 0 0 0 .42 2.76C.222 3.268.087 3.85.048 4.7.01 5.555 0 5.827 0 8.001c0 2.172.01 2.444.048 3.297.04.852.174 1.433.372 1.942.205.526.478.972.923 1.417.444.445.89.719 1.416.923.51.198 1.09.333 1.942.372C5.555 15.99 5.827 16 8 16s2.444-.01 3.298-.048c.851-.04 1.434-.174 1.943-.372a3.916 3.916 0 0 0 1.416-.923c.445-.445.718-.891.923-1.417.197-.509.332-1.09.372-1.942C15.99 10.445 16 10.173 16 8s-.01-2.445-.048-3.299c-.04-.851-.175-1.433-.372-1.941a3.926 3.926 0 0 0-.923-1.417A3.911 3.911 0 0 0 13.24.42c-.51-.198-1.092-.333-1.943-.372C10.443.01 10.172 0 7.998 0h.003zm-.717 1.442h.718c2.136 0 2.389.007 3.232.046.78.035 1.204.166 1.486.275.373.145.64.319.92.599.28.28.453.546.598.92.11.281.24.705.275 1.485.039.843.047 1.096.047 3.231s-.008 2.389-.047 3.232c-.035.78-.166 1.203-.275 1.485a2.47 2.47 0 0 1-.599.919c-.28.28-.546.453-.92.598-.28.11-.704.24-1.485.276-.843.038-1.096.047-3.232.047s-2.39-.009-3.233-.047c-.78-.036-1.203-.166-1.485-.276a2.478 2.478 0 0 1-.92-.598 2.48 2.48 0 0 1-.6-.92c-.109-.281-.24-.705-.275-1.485-.038-.843-.046-1.096-.046-3.233 0-2.136.008-2.388.046-3.231.036-.78.166-1.204.276-1.486.145-.373.319-.64.599-.92.28-.28.546-.453.92-.598.282-.11.705-.24 1.485-.276.738-.034 1.024-.044 2.515-.045v.002zm4.988 1.328a.96.96 0 1 0 0 1.92.96.96 0 0 0 0-1.92zm-4.27 1.122a4.109 4.109 0 1 0 0 8.217 4.109 4.109 0 0 0 0-8.217zm0 1.441a2.667 2.667 0 1 1 0 5.334 2.667 2.667 0 0 1 0-5.334z",
  facebook: "M16 8.049c0-4.446-3.582-8.05-8-8.05C3.58 0-.002 3.603-.002 8.05c0 4.017 2.926 7.347 6.75 7.951v-5.625h-2.03V8.05H6.75V6.275c0-2.017 1.195-3.131 3.022-3.131.876 0 1.791.157 1.791.157v1.98h-1.009c-.993 0-1.303.621-1.303 1.258v1.51h2.218l-.354 2.326H9.25V16c3.824-.604 6.75-3.934 6.75-7.951z",
  twitter: "M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z",
  youtube: "M8.051 1.999h.089c.822.003 4.987.033 6.11.335a2.01 2.01 0 0 1 1.415 1.42c.101.38.172.883.22 1.402l.01.104.022.26.008.104c.065.914.073 1.77.074 1.957v.075c-.001.194-.01 1.108-.082 2.06l-.008.105-.009.104c-.05.572-.124 1.14-.235 1.558a2.007 2.007 0 0 1-1.415 1.42c-1.16.312-5.569.334-6.18.335h-.142c-.309 0-1.587-.006-2.927-.052l-.17-.006-.087-.004-.171-.007-.171-.007c-1.11-.049-2.167-.128-2.654-.26a2.007 2.007 0 0 1-1.415-1.419c-.111-.417-.185-.986-.235-1.558L.09 9.82l-.008-.104A31.4 31.4 0 0 1 0 7.68v-.123c.002-.215.01-.958.064-1.778l.007-.103.003-.052.008-.104.022-.26.01-.104c.048-.519.119-1.023.22-1.402a2.007 2.007 0 0 1 1.415-1.42c.487-.13 1.544-.21 2.654-.26l.17-.007.172-.006.086-.003.171-.007A99.788 99.788 0 0 1 7.858 2h.193zM6.4 5.209v4.818l4.157-2.408L6.4 5.209z"
};

const listItems = ["Linkedin", "Instagram", "Facebook", "Twitter", "Youtube"];

const iconTemplate = (path) => {
  return `
    
      
    
  `;
};

const listItemTemplate = (text, translateValue) => {
  return `
    
  `;
};

const renderListItems = () => {
  dropdownList.innerHTML += listItems
    .map((item, index) => {
      return listItemTemplate(item, 100 * index);
    })
    .join("");
};

window.addEventListener("load", () => {
  renderListItems();
});

const setDropdownProps = (deg, ht, opacity) => {
  root.style.setProperty("--rotate-arrow", deg !== 0 ? deg + "deg" : 0);
  root.style.setProperty("--dropdown-height", ht !== 0 ? ht + "rem" : 0);
  root.style.setProperty("--list-opacity", opacity);
};

mainButton.addEventListener("click", () => {
  const listWrapperSizes = 3.5; // margins, paddings & borders
  const dropdownOpenHeight = 4.6 * listItems.length + listWrapperSizes;
  const currDropdownHeight =
    root.style.getPropertyValue("--dropdown-height") || "0";

  currDropdownHeight === "0"
    ? setDropdownProps(180, dropdownOpenHeight, 1)
    : setDropdownProps(0, 0, 0);
});

dropdownList.addEventListener("mouseover", (e) => {
  const translateValue = e.target.dataset.translateValue;
  root.style.setProperty("--translate-value", translateValue);
});

dropdownList.addEventListener("click", (e) => {
  const clickedItemText = e.target.innerText.toLowerCase().trim();
  const clickedItemIcon = icons[clickedItemText];

  dropdownTitleIcon.innerHTML = iconTemplate(clickedItemIcon);
  dropdownTitle.innerHTML = clickedItemText;
  setDropdownProps(0, 0, 0);
});

dropdownList.addEventListener("mousemove", (e) => {
  const iconSize = root.style.getPropertyValue("--floating-icon-size") || 0;
  const x = e.clientX - dropdownList.getBoundingClientRect().x;
  const y = e.clientY - dropdownList.getBoundingClientRect().y;
  const targetText = e.target.innerText.toLowerCase().trim();
  const hoverItemText = icons[targetText];

  floatingIcon.innerHTML = iconTemplate(hoverItemText);
  root.style.setProperty("--floating-icon-left", x - iconSize / 2 + "px");
  root.style.setProperty("--floating-icon-top", y - iconSize / 2 + "px");
});
                        

Live Demo: Dropdown Menu

Watch a live demonstration of the Dropdown Menu below.

Final Thoughts

This custom dropdown menu is a perfect example of how thoughtful design and modern web technologies can elevate a simple UI component. The combination of dynamic JavaScript for content and advanced CSS for animations results in a component that is both functional and engaging. It's a lightweight and performant solution for navigation menus, social links, or any selection-based interface.

You can easily customize this component by:

  • Modifying the listItems array in JavaScript to change the options.
  • Adding new SVG paths to the icons object in JavaScript.
  • Adjusting the CSS variables at the top of the CSS file to change colors, timings, and sizes.
  • Integrating it into forms or navigation bars as a stylish selector.

Pro Tip

Leverage CSS Custom Properties for Theming. This project makes extensive use of variables like --primary-bg-color and --transition-time. By centralizing these values, you can change the entire look and feel of the dropdown in seconds without searching for individual styles.

Ready to Use This Project?

Click the button below to download the full source code. Download will be ready in 10 seconds.

Stay Updated

Receive coding tips and resources updates. No spam.

We respect your privacy. Unsubscribe at any time.