Learn how to create a multi-level dropdown menu using HTML, CSS, and JavaScript. Step by step guide to make responsive navigation menus for websites.
Table of Contents
Navigation menus are very important for websites. They help visitors explore pages easily. A multi-level dropdown menu makes navigation even better by allowing more than one level of links. For example, under Products, you can have Product B, and under that, more options like Sub-Product B1.
In this blog, we will learn how to create a multi-level dropdown menu using HTML, CSS, and JavaScript step by step.
Prerequisites
Before starting, you should know:
- Basics of HTML (to build menu structure)
- Basics of CSS (to style the menu)
- Basics of JavaScript (to add interactivity)
- A code editor like VS Code
- A web browser for testing
Source Code
Step 1 (HTML Code):
Create an HTML file and add the main menu with submenus using <ul> and <li> tags. Let's break down the HTML code step by step:
1. Document Setup
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Multi-Level Dropdown Menu</title>
<!DOCTYPE html>→ Tells the browser this is an HTML5 document.<html lang="en">→ Language is English.<meta charset="UTF-8">→ Character encoding (supports all major characters/symbols).<meta name="viewport" content="width=device-width, initial-scale=1.0">→ Ensures the page is responsive on mobile devices.<title>→ Sets the title shown in the browser tab.
2. Fonts & Styles
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="styles.css">
- Preconnect + link to Google Fonts (Inter family).
- External stylesheet styles.css will style the menu.
3. Menu Container
<div class="menu-container">
<ul class="main-menu">
- menu-container → A wrapper for the navigation.
- main-menu → The top-level navigation list.
4. Menu Items
<li><a href="#">Home</a></li>
- Each
<li>is a menu item. <a>is the clickable link.
5. Dropdown (First Level)
<li class="has-submenu">
<a href="#">
<span>Products</span>
<!-- Down Arrow Icon -->
<svg class="arrow-icon down" ...>
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</a>
<ul class="sub-menu">
<li><a href="#">Product A</a></li>
...
</ul>
</li>
- has-submenu → Marks an item that has a dropdown.
- Inside, an
<a>link with a down arrow icon (SVG) indicates a dropdown. - Nested
<ul class="sub-menu">→ This is the dropdown list.
6. Multi-Level Dropdown (Nested Dropdown)
<li class="has-submenu">
<a href="#">
<span>Product B</span>
<!-- Right Arrow Icon -->
<svg class="arrow-icon right" ...>
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</a>
<ul class="sub-menu">
<li><a href="#">Sub-Product B1</a></li>
<li><a href="#">Sub-Product B2</a></li>
</ul>
</li>
- Product B has its own submenu (B1, B2).
- Arrow points right → means it expands sideways.
- This creates a multi-level dropdown menu.
7. Another Dropdown Example (Services)
<li class="has-submenu">
<a href="#">
<span>Services</span>
<svg class="arrow-icon down" ...></svg>
</a>
<ul class="sub-menu">
<li><a href="#">Web Development</a></li>
<li><a href="#">App Development</a></li>
<li><a href="#">UI/UX Design</a></li>
<li><a href="#">SEO & Marketing</a></li>
</ul>
</li>
- Works the same way as Products.
- Shows a list of services in a dropdown format.
8. Other Menu Items
<li><a href="#">About Us</a></li>
<li><a href="#">Contact</a></li>
- Regular links without dropdowns.
9. JavaScript
<script src="script.js"></script>
- External JS file controls dropdown behavior (e.g., toggle open/close on click, close when clicking outside).
Step 2 (CSS Code):
Now, style the menu with CSS to create a clean dropdown effect. Let's break down the CSS code step by step:
1. Base Styles & Variables
:root {
--primary-bg: #111827;
--secondary-bg-glass: rgba(31, 41, 55, 0.5);
--accent-gradient: linear-gradient(90deg, #3b82f6, #8b5cf6);
--text-color: #f9fafb;
--text-muted-color: #9ca3af;
--border-color-glass: rgba(255, 255, 255, 0.1);
--shadow-color: rgba(0, 0, 0, 0.3);
--border-radius: 12px;
--transition-speed: 0.3s;
}
- Declares CSS variables for colors, shadows, border-radius, and transition speed.
- Makes the theme consistent and easy to update later.
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
- Resets margin/padding for all elements.
- Uses box-sizing: border-box so width includes padding & border.
body {
font-family: 'Inter', sans-serif;
background-color: var(--primary-bg);
background-image: radial-gradient(...);
color: var(--text-color);
display: flex;
justify-content: center;
align-items: flex-start;
padding-top: 5rem;
min-height: 100vh;
}
- Dark background + subtle radial gradient (adds depth).
- Uses the Inter font.
- Flexbox centers the menu horizontally.
- Padding from the top ensures spacing.
2. Menu Container
.menu-container {
width: fit-content;
}
- Container size adjusts exactly to the menu width.
3. Main Menu
.main-menu {
list-style: none;
display: flex;
background-color: transparent;
padding: 0;
border-radius: var(--border-radius);
}
- Horizontal navigation (display: flex).
- Removes list bullets.
.main-menu li {
position: relative;
}
- Needed because dropdowns are absolutely positioned inside li.
.main-menu > li > a {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 1rem 1.5rem;
color: var(--text-muted-color);
text-decoration: none;
font-weight: 500;
transition: color, background-color;
position: relative;
border-radius: var(--border-radius);
margin: 0 0.25rem;
}
- Top-level links styled with padding, muted color, hover effects, and a nice gap between text and icons.
.main-menu > li > a:hover {
color: var(--text-color);
background-color: rgba(255, 255, 255, 0.05);
}
- Subtle hover highlight.
4. Animated Underline
.main-menu > li > a::after {
content: '';
position: absolute;
bottom: 6px;
left: 50%;
transform: translateX(-50%);
width: 0;
height: 2px;
background: var(--accent-gradient);
transition: width var(--transition-speed) ease;
}
.main-menu > li > a:hover::after {
width: 40%;
}
- Creates a gradient underline that animates when hovering.
5. Dropdown (Glass Effect)
.sub-menu {
list-style: none;
position: absolute;
top: calc(100% + 10px);
left: 0;
background-color: var(--secondary-bg-glass);
backdrop-filter: blur(10px);
border: 1px solid var(--border-color-glass);
border-radius: var(--border-radius);
padding: 0.5rem;
min-width: 240px;
box-shadow: 0 10px 30px var(--shadow-color);
opacity: 0;
visibility: hidden;
transform: scale(0.95) translateY(10px);
transform-origin: top;
transition: opacity, visibility, transform;
}
- Dropdown has glassmorphism style (blurred background).
- Initially hidden (opacity:0; visibility:hidden).
- Animated opening effect: scales & slides down slightly.
.sub-menu .sub-menu {
top: -0.6rem;
left: 100%;
}
- Nested submenus open to the right side.
li:hover > .sub-menu,
li.active > .sub-menu {
opacity: 1;
visibility: visible;
transform: scale(1) translateY(0);
}
- Shows dropdown on hover (desktop).
- Shows dropdown when parent has active class (mobile).
6. Dropdown Links
.sub-menu li a {
display: flex;
justify-content: space-between;
padding: 0.8rem 1rem;
color: var(--text-muted-color);
text-decoration: none;
border-radius: 8px;
transition: background-color, color;
white-space: nowrap;
}
.sub-menu li a:hover {
color: var(--text-color);
background: var(--accent-gradient);
}
- Submenu links have muted color → turn bright gradient on hover.
7. Arrow Icons
.arrow-icon {
width: 16px;
height: 16px;
stroke-width: 2;
stroke: var(--text-muted-color);
transition: transform, stroke;
}
li:hover > a > .arrow-icon.down,
li.active > a > .arrow-icon.down {
transform: rotate(180deg);
}
li:hover > a > .arrow-icon.right {
transform: translateX(4px);
}
- Arrow icons rotate (↓ become ↑ when open).
- Right arrows slide slightly for animation.
8. Responsive (Mobile ≤ 992px)
@media (max-width: 992px) {
.menu-container {
width: 100%;
max-width: 350px;
padding: 0 1rem;
}
.main-menu {
flex-direction: column;
width: 100%;
background-color: var(--secondary-bg-glass);
border: 1px solid var(--border-color-glass);
padding: 0.5rem;
}
- Menu turns into a vertical sidebar.
- Glass background is applied to the main container.
.sub-menu {
position: static;
border: none;
box-shadow: none;
opacity: 1;
visibility: visible;
transform: none;
padding-left: 1rem;
background-color: transparent;
display: none; /* Hidden until activated */
}
li.active > .sub-menu {
display: block;
}
- In mobile, submenus are hidden by default → shown only when parent has active.
- No glass effect, no shadows → simpler style for mobile.
/* --- Base Styles & Variables --- */
:root {
--primary-bg: #111827;
--secondary-bg-glass: rgba(31, 41, 55, 0.5);
--accent-gradient: linear-gradient(90deg, #3b82f6, #8b5cf6);
--text-color: #f9fafb;
--text-muted-color: #9ca3af;
--border-color-glass: rgba(255, 255, 255, 0.1);
--shadow-color: rgba(0, 0, 0, 0.3);
--border-radius: 12px;
--transition-speed: 0.3s;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
background-color: var(--primary-bg);
background-image: radial-gradient(
circle at 10% 20%,
rgba(131, 58, 180, 0.1),
transparent 50%
),
radial-gradient(circle at 80% 90%, rgba(253, 29, 29, 0.1), transparent 50%);
color: var(--text-color);
display: flex;
justify-content: center;
align-items: flex-start;
padding-top: 5rem;
min-height: 100vh;
}
/* --- Menu Container --- */
.menu-container {
width: fit-content;
}
/* --- Main Menu --- */
.main-menu {
list-style: none;
display: flex;
background-color: transparent;
padding: 0;
border-radius: var(--border-radius);
}
.main-menu li {
position: relative;
}
.main-menu > li > a {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 1rem 1.5rem;
color: var(--text-muted-color);
text-decoration: none;
font-weight: 500;
transition: color var(--transition-speed) ease,
background-color var(--transition-speed) ease;
position: relative;
overflow: hidden;
border-radius: var(--border-radius);
margin: 0 0.25rem;
}
.main-menu > li > a:hover {
color: var(--text-color);
background-color: rgba(255, 255, 255, 0.05);
}
/* --- Animated Underline Effect --- */
.main-menu > li > a::after {
content: '';
position: absolute;
bottom: 6px;
left: 50%;
transform: translateX(-50%);
width: 0;
height: 2px;
background: var(--accent-gradient);
transition: width var(--transition-speed) ease;
}
.main-menu > li > a:hover::after {
width: 40%;
}
/* --- Dropdown Styles with Frosted Glass Effect --- */
.sub-menu {
list-style: none;
position: absolute;
top: calc(100% + 10px);
left: 0;
/* Glassmorphism */
background-color: var(--secondary-bg-glass);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid var(--border-color-glass);
border-radius: var(--border-radius);
padding: 0.5rem;
min-width: 240px;
box-shadow: 0 10px 30px var(--shadow-color);
z-index: 1000;
/* New Animation */
opacity: 0;
visibility: hidden;
transform: scale(0.95) translateY(10px);
transform-origin: top;
transition: opacity var(--transition-speed) ease,
visibility var(--transition-speed) ease,
transform var(--transition-speed) ease;
}
/* Nested sub-menus */
.sub-menu .sub-menu {
top: -0.6rem;
left: 100%;
}
/* Show dropdown on hover (for desktop) */
li:hover > .sub-menu {
opacity: 1;
visibility: visible;
transform: scale(1) translateY(0);
}
/* Show dropdown when parent is active (for mobile) */
li.active > .sub-menu {
opacity: 1;
visibility: visible;
transform: scale(1) translateY(0);
}
.sub-menu li a {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.8rem 1rem;
color: var(--text-muted-color);
text-decoration: none;
border-radius: 8px;
transition: background-color var(--transition-speed) ease,
color var(--transition-speed) ease;
white-space: nowrap;
position: relative;
}
.sub-menu li a:hover {
color: var(--text-color);
background: var(--accent-gradient);
}
.sub-menu li a:hover .arrow-icon {
color: var(--text-color);
}
/* --- Arrow Icons --- */
.arrow-icon {
width: 16px;
height: 16px;
stroke-width: 2;
stroke: var(--text-muted-color);
transition: transform var(--transition-speed) ease,
stroke var(--transition-speed) ease;
}
li:hover > a > .arrow-icon.down,
li.active > a > .arrow-icon.down {
transform: rotate(180deg);
}
li:hover > a > .arrow-icon.right {
transform: translateX(4px);
}
/* --- Responsive Design --- */
@media (max-width: 992px) {
.menu-container {
width: 100%;
max-width: 350px;
padding: 0 1rem;
}
.main-menu {
flex-direction: column;
width: 100%;
background-color: var(--secondary-bg-glass);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid var(--border-color-glass);
padding: 0.5rem;
}
.main-menu > li > a {
margin: 0.25rem 0;
}
.main-menu > li > a::after {
display: none;
}
.sub-menu {
position: static;
border: none;
box-shadow: none;
opacity: 1;
visibility: visible;
transform: none;
padding-left: 1rem;
min-width: auto;
background-color: transparent;
backdrop-filter: none;
display: none; /* Sub-menus are closed by default */
}
.sub-menu .sub-menu {
padding-left: 1rem;
}
li.active > .sub-menu {
display: block;
}
li:hover > .sub-menu {
opacity: 0;
visibility: hidden;
}
li.active:hover > .sub-menu {
opacity: 1;
visibility: visible;
}
li > a {
justify-content: space-between;
}
li.active > a > .arrow-icon.down {
transform: rotate(180deg);
}
} Step 3 (JavaScript Code):
Now, let’s make the menu work on mobile and clickable. Let's break down the JS code step by step:
1. DOMContentLoaded
document.addEventListener('DOMContentLoaded', function () {
- Runs the script only after the whole HTML is loaded.
- Prevents errors if the script loads before elements exist.
2. Selectors
const submenuItems = document.querySelectorAll('.has-submenu > a');
const mainMenu = document.querySelector('.main-menu');
- submenuItems → Selects all
<a>links that belong to menu items with a submenu. - mainMenu → Selects the whole navigation menu container.
3. Click Event for Submenu Links
submenuItems.forEach((item) => {
item.addEventListener('click', function (event) {
- Loops through each submenu link (Products, Services, etc.).
- Adds a click listener.
4. Mobile Only Behavior
if (window.innerWidth <= 992) {
event.preventDefault();
const parentLi = this.parentElement;
- If screen width is 992px or smaller (tablet/mobile):
- event.preventDefault() stops the link from navigating.
- Gets the parent
<li>(the menu item).
5. Accordion Behavior (Close Others)
const siblingSubmenus = parentLi.parentElement.querySelectorAll(
'.has-submenu.active'
);
siblingSubmenus.forEach((sibling) => {
if (sibling !== parentLi) {
sibling.classList.remove('active');
}
});
- Looks for other open submenus at the same level.
- Closes them by removing the active.
- This makes it behave like an accordion → only one submenu stays open.
6. Toggle Current Submenu
parentLi.classList.toggle('active');
- If submenu is closed → opens it (.active added).
- If already open → closes it (.active removed).
7. Close When Clicking Outside
document.addEventListener('click', function (event) {
if (!mainMenu.contains(event.target)) {
document
.querySelectorAll('.has-submenu.active')
.forEach((activeItem) => {
activeItem.classList.remove('active');
});
}
});
- Adds another listener to the whole page.
- If you click outside the menu, it:
- Finds all currently open submenus (.active).
- Closes them.
document.addEventListener('DOMContentLoaded', function () {
// Dropdown toggle for mobile
const submenuItems = document.querySelectorAll('.has-submenu > a');
const mainMenu = document.querySelector('.main-menu');
submenuItems.forEach((item) => {
item.addEventListener('click', function (event) {
// Only prevent default for dropdowns on smaller screens
if (window.innerWidth <= 992) {
event.preventDefault();
const parentLi = this.parentElement;
// Close other open submenus at the same level to act like an accordion
const siblingSubmenus = parentLi.parentElement.querySelectorAll(
'.has-submenu.active'
);
siblingSubmenus.forEach((sibling) => {
if (sibling !== parentLi) {
sibling.classList.remove('active');
}
});
// Toggle current submenu
parentLi.classList.toggle('active');
}
});
});
document.addEventListener('click', function (event) {
if (!mainMenu.contains(event.target)) {
document
.querySelectorAll('.has-submenu.active')
.forEach((activeItem) => {
activeItem.classList.remove('active');
});
}
});
});Final Output:
Conclusion:
With this tutorial, you have built a multi-level dropdown menu using HTML, CSS, and JavaScript. This menu works on both desktop (hover) and mobile (click). It is responsive, simple, and easy to customize.
A well-structured menu improves user experience and makes your website look professional. You can further enhance it by adding animations or icons.
That’s a wrap!
I hope you enjoyed this post. Now, with these examples, you can create your own amazing page.
Did you like it? Let me know in the comments below 🔥 and you can support me by buying me a coffee
And don’t forget to sign up to our email newsletter so you can get useful content like this sent right to your inbox!
Thanks!
Faraz 😊


