Introduction to animated menus
Navigation is the cornerstone of user experience on a website. A well-designed and animated menu does more than just function: it guides the user, provides clear visual feedback, and reinforces your interface's identity.
In this complete guide, we'll explore 7 types of animated menus you can implement with modern CSS and minimal JavaScript. Each technique comes with an interactive demo and the complete code.
All animations in this guide use CSS for transitions and transformations. JavaScript only steps in to toggle classes, ensuring optimal performance.
1. Animated hamburger menu (transform to X)
The hamburger icon has become a standard for mobile menus. The animation that transforms the three bars into a cross (X) clearly signals to the user that the menu is open and can be closed.
Animation principle
The animation relies on three simultaneous transformations:
- Top bar: 45-degree rotation + translation downward
- Middle bar: disappears (opacity or scale)
- Bottom bar: -45-degree rotation + translation upward
.hamburger {
width: 48px;
height: 48px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 6px;
cursor: pointer;
background: transparent;
border: 1px solid rgba(255,255,255,0.1);
border-radius: 12px;
}
.hamburger span {
display: block;
width: 24px;
height: 2px;
background: white;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
transform-origin: center;
}
/* Active state - transform to X */
.hamburger.active span:nth-child(1) {
transform: rotate(45deg) translate(5px, 6px);
}
.hamburger.active span:nth-child(2) {
opacity: 0;
transform: scaleX(0);
}
.hamburger.active span:nth-child(3) {
transform: rotate(-45deg) translate(5px, -6px);
}
2. Slide-in menu (drawer)
The drawer menu slides in from the side of the screen, often accompanied by a dark overlay. This is the standard pattern for mobile menus that contain many options.
.slide-menu {
position: fixed;
top: 0;
left: 0;
width: 280px;
height: 100vh;
background: #12121a;
transform: translateX(-100%);
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
z-index: 1001;
}
.slide-menu.active {
transform: translateX(0);
}
/* Dark overlay */
.slide-menu-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.5);
opacity: 0;
visibility: hidden;
transition: all 0.3s;
z-index: 1000;
}
.menu-open .slide-menu-overlay {
opacity: 1;
visibility: visible;
}
3. Dropdown with transitions
Dropdown menus are essential for organizing hierarchical navigation. A smooth animation with fade + slide conveys quality and guides the user's eye.
.dropdown {
position: relative;
}
.dropdown-menu {
position: absolute;
top: calc(100% + 8px);
left: 0;
min-width: 220px;
background: #12121a;
border: 1px solid rgba(255,255,255,0.08);
border-radius: 12px;
padding: 8px;
box-shadow: 0 20px 40px rgba(0,0,0,0.3);
/* Initial hidden state */
opacity: 0;
visibility: hidden;
transform: translateY(-10px) scale(0.95);
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
.dropdown.active .dropdown-menu {
opacity: 1;
visibility: visible;
transform: translateY(0) scale(1);
}
/* Arrow animation */
.dropdown-trigger svg {
transition: transform 0.3s;
}
.dropdown.active .dropdown-trigger svg {
transform: rotate(180deg);
}
4. Animated tabs with sliding indicator
Tabs with an indicator that slides between them provide excellent visual feedback. The user clearly sees the active tab and the transition is smooth and satisfying.
.tabs {
display: flex;
position: relative;
background: #0a0a0f;
padding: 4px;
border-radius: 12px;
}
.tab {
padding: 12px 24px;
background: transparent;
border: none;
color: #a1a1aa;
cursor: pointer;
z-index: 1;
transition: color 0.3s;
}
.tab.active {
color: white;
}
/* Indicateur glissant */
.tab-indicator {
position: absolute;
height: calc(100% - 8px);
top: 4px;
background: #6366f1;
border-radius: 8px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
z-index: 0;
}
const tabs = document.querySelectorAll('.tab');
const indicator = document.querySelector('.tab-indicator');
function moveIndicator(tab) {
indicator.style.width = tab.offsetWidth + 'px';
indicator.style.left = tab.offsetLeft + 'px';
}
tabs.forEach(tab => {
tab.addEventListener('click', () => {
tabs.forEach(t => t.classList.remove('active'));
tab.classList.add('active');
moveIndicator(tab);
});
});
// Position initiale
moveIndicator(document.querySelector('.tab.active'));
5. Mega menu with reveal
Mega menus are perfect for content-rich sites. The reveal animation creates a sense of depth and allows many options to be displayed in an organized manner.
.mega-menu-content {
position: absolute;
top: calc(100% + 12px);
left: 0;
right: 0;
background: #12121a;
border-radius: 16px;
padding: 24px;
box-shadow: 0 20px 40px rgba(0,0,0,0.3);
/* Animation reveal */
opacity: 0;
visibility: hidden;
transform: translateY(-10px);
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.mega-menu.active .mega-menu-content {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
.mega-menu-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 16px;
}
6. Animated underline on hover
The animated underline effect is an elegant classic for navigation links. The line draws from left to right on hover, creating a subtle yet effective effect.
.nav-link {
position: relative;
padding: 12px 0;
color: #a1a1aa;
transition: color 0.3s;
}
.nav-link::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 0;
height: 2px;
background: linear-gradient(90deg, #6366f1, #8b5cf6);
transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.nav-link:hover {
color: white;
}
.nav-link:hover::after,
.nav-link.active::after {
width: 100%;
}
For an underline that appears from the center, use left: 50% and transform: translateX(-50%) in addition to animating the width.
7. Menu accessibility
A beautiful menu is not enough: it must be accessible to all users, including those who navigate by keyboard or with a screen reader.
Essential ARIA attributes
- aria-expanded: indicates whether a menu is open or closed
- aria-haspopup: signals that an element triggers a menu
- aria-controls: links the button to the menu it controls
- role="menu" and role="menuitem": define the structure
<div class="dropdown">
<button
class="dropdown-trigger"
aria-expanded="false"
aria-haspopup="true"
aria-controls="dropdown-menu"
>
Menu
</button>
<ul
id="dropdown-menu"
role="menu"
aria-label="Main menu"
>
<li role="menuitem">Option 1</li>
<li role="menuitem">Option 2</li>
</ul>
</div>
Keyboard navigation
An accessible menu must support:
- Enter / Space: open/close the menu
- Escape: close the menu
- Up/down arrows: navigate between items
- Tab: exit the menu
dropdown.addEventListener('keydown', (e) => {
const items = dropdown.querySelectorAll('[role="menuitem"]');
const currentIndex = Array.from(items).indexOf(document.activeElement);
switch(e.key) {
case 'Escape':
closeDropdown();
trigger.focus();
break;
case 'ArrowDown':
e.preventDefault();
const nextIndex = (currentIndex + 1) % items.length;
items[nextIndex].focus();
break;
case 'ArrowUp':
e.preventDefault();
const prevIndex = currentIndex <= 0 ? items.length - 1 : currentIndex - 1;
items[prevIndex].focus();
break;
}
});
Respect user preferences with @media (prefers-reduced-motion: reduce) to disable or reduce animations for people sensitive to motion.
Conclusion
Animated menus are much more than an aesthetic detail: they guide the user, provide visual feedback, and reinforce your interface identity. By mastering the techniques presented in this guide, you can create smooth and professional navigation.
Key takeaways:
- Use
transformandopacityfor performant animations - The
cubic-bezier(0.4, 0, 0.2, 1)function produces natural transitions - Combine
visibilitywithopacityfor clean transitions - Never forget accessibility (ARIA, keyboard navigation)
- Test on mobile and respect
prefers-reduced-motion
Find ready-to-use navigation components in our effects library, with copyable and customizable code.