Dropdown menus are essential for clean navigation, helping you organize links without cluttering your interface. In this tutorial, we'll build a responsive dropdown menu using Tailwind CSS, inspired by the EXHIBIT portfolio template by TemplatesJungle. We'll cover both desktop hover-based dropdowns and mobile-friendly click-based menus.
What We're Building
We'll create a navigation system that:
- Shows dropdown menus on hover for desktop users
- Uses click-to-toggle dropdowns on mobile devices
- Adapts smoothly between screen sizes
- Includes animated icons and smooth transitions
Here's a preview of the desktop dropdown structure from the EXHIBIT template:
<!-- Portfolio Dropdown - Desktop -->
<div class="relative group">
<button class="font-label-caps text-label-caps uppercase tracking-widest flex items-center gap-1">
Portfolio
<span class="material-symbols-outlined text-sm transition-transform duration-200 group-hover:rotate-180">
expand_more
</span>
</button>
<div class="absolute top-full left-0 mt-2 w-56 bg-surface border rounded-lg shadow-lg opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 z-50">
<a href="portfolio.html" class="block px-6 py-3 hover:bg-surface-container-low transition-colors duration-200 border-b">
All Projects
</a>
<a href="portfolio-detail.html" class="block px-6 py-3 hover:bg-surface-container-low transition-colors duration-200">
Project Detail
</a>
</div>
</div>
Understanding the Core Concept
The magic behind Tailwind dropdowns lies in two key utility groups:
1. The group Modifier
The group class lets you style child elements based on a parent's state. This is perfect for dropdowns where hovering the parent should reveal the menu . When you add group to a parent element, you can use group-hover:, group-focus:, and other variants on its children .
2. Responsive Utilities
Tailwind's responsive prefixes (sm:, md:, lg:) allow you to show different navigation patterns at different screen sizes. The desktop dropdowns appear on medium screens and above, while a hamburger menu with clickable dropdowns takes over on mobile .
Building the Desktop Dropdown
Step 1: Structure the Parent Container
Start with a container that holds both the trigger button and the dropdown menu:
<div class="relative group">
<!-- Trigger button and dropdown menu go here -->
</div>
The relative class positions the dropdown menu correctly, while group enables the hover state styling.
Step 2: Add the Trigger Button
The trigger button is what users interact with to reveal the menu. Include an icon that rotates when the menu is open:
<button class="font-label-caps text-label-caps uppercase tracking-widest flex items-center gap-1">
Portfolio
<span class="material-symbols-outlined text-sm transition-transform duration-200 group-hover:rotate-180">
expand_more
</span>
</button>
Key classes:
-
group-hover:rotate-180rotates the icon when the parent is hovered -
transition-transform duration-200ensures a smooth 200ms animation
Step 3: Build the Dropdown Menu
The dropdown menu appears below the trigger. Use absolute positioning and visibility toggles:
<div class="absolute top-full left-0 mt-2 w-56 bg-surface border rounded-lg shadow-lg opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 z-50">
<a href="#" class="block px-6 py-3 hover:bg-surface-container-low transition-colors duration-200 border-b">
All Projects
</a>
<a href="#" class="block px-6 py-3 hover:bg-surface-container-low transition-colors duration-200">
Project Detail
</a>
</div>
Breakdown of the classes:
-
absolute top-full left-0positions the menu directly below the trigger -
mt-2adds spacing between trigger and menu -
opacity-0 invisiblehides the menu initially -
group-hover:opacity-100 group-hover:visiblereveals it on hover -
transition-all duration-200creates a smooth fade effect -
z-50ensures the menu appears above other content
Building the Mobile Dropdown
Mobile dropdowns work differently—they need to respond to clicks rather than hovers. The EXHIBIT template uses a combination of JavaScript and Tailwind classes for this.
Step 1: Create the Mobile Dropdown Structure
<div class="mobile-dropdown border-b border-surface-container-highest">
<button class="mobile-dropdown-toggle w-full flex items-center justify-between py-3 px-2 font-label-caps text-label-caps uppercase tracking-widest hover:bg-surface-container-low rounded transition-colors duration-200" data-dropdown="mobile-portfolio">
<span>Portfolio</span>
<span class="material-symbols-outlined text-sm transition-transform duration-200">expand_more</span>
</button>
<div class="mobile-dropdown-menu hidden pl-4 space-y-1 pb-2" id="mobile-portfolio">
<a href="portfolio.html" class="block py-2 px-2 hover:bg-surface-container-low rounded transition-colors duration-200">
All Projects
</a>
<a href="portfolio-detail.html" class="block py-2 px-2 hover:bg-surface-container-low rounded transition-colors duration-200">
Project Detail
</a>
</div>
</div>
Step 2: Add the Toggle Logic
Here's the JavaScript that handles the click-to-toggle functionality:
function toggleMobileDropdown(button) {
const dropdownMenu = button.nextElementSibling;
const icon = button.querySelector('.material-symbols-outlined');
if (dropdownMenu) {
// Toggle the current dropdown
const isOpen = dropdownMenu.classList.contains('open');
if (isOpen) {
dropdownMenu.classList.remove('open');
dropdownMenu.style.display = 'none';
button.classList.remove('open');
if (icon) {
icon.style.transform = 'rotate(0deg)';
}
} else {
// Close all other dropdowns first
closeAllMobileDropdowns();
// Open this one
dropdownMenu.classList.add('open');
dropdownMenu.style.display = 'block';
button.classList.add('open');
if (icon) {
icon.style.transform = 'rotate(180deg)';
}
}
}
}
function closeAllMobileDropdowns() {
document.querySelectorAll('.mobile-dropdown-menu').forEach(function(menu) {
menu.classList.remove('open');
menu.style.display = 'none';
});
document.querySelectorAll('.mobile-dropdown-toggle').forEach(function(toggle) {
toggle.classList.remove('open');
const icon = toggle.querySelector('.material-symbols-outlined');
if (icon) {
icon.style.transform = 'rotate(0deg)';
}
});
}
// Attach event listeners to dropdown toggles
document.querySelectorAll('.mobile-dropdown-toggle').forEach(function(toggle) {
toggle.addEventListener('click', function(e) {
e.stopPropagation();
toggleMobileDropdown(this);
});
});
Step 3: Add Custom CSS for Transitions
Add these styles to your CSS file to control the dropdown animations:
.mobile-dropdown-toggle .material-symbols-outlined {
transition: transform 0.3s ease;
}
.mobile-dropdown-toggle.open .material-symbols-outlined {
transform: rotate(180deg);
}
.mobile-dropdown-menu {
transition: all 0.3s ease;
overflow: hidden;
}
.mobile-dropdown-menu.open {
display: block !important;
}
Making It Responsive
The key to responsive dropdowns is using Tailwind's responsive prefixes to show/hide different navigation patterns:
<!-- Desktop Navigation - Hidden on mobile -->
<nav class="hidden md:flex items-center gap-6 lg:gap-8">
<!-- Desktop dropdowns here -->
</nav>
<!-- Mobile Navigation - Hidden on desktop -->
<div class="md:hidden">
<!-- Mobile dropdowns here -->
</div>
The hidden md:flex pattern shows the desktop navigation on medium screens and above, while md:hidden hides the mobile menu on larger screens .
Complete Example
Here's a complete navigation system combining both approaches:
<!-- Desktop Navigation with Dropdowns -->
<nav class="hidden md:flex items-center gap-6 lg:gap-8">
<a href="#" class="font-label-caps text-label-caps uppercase tracking-widest hover:text-primary transition-colors duration-200">Home</a>
<!-- Portfolio Dropdown -->
<div class="relative group">
<button class="font-label-caps text-label-caps uppercase tracking-widest flex items-center gap-1 hover:text-primary transition-colors duration-200">
Portfolio
<span class="material-symbols-outlined text-sm transition-transform duration-200 group-hover:rotate-180">expand_more</span>
</button>
<div class="absolute top-full left-0 mt-2 w-56 bg-surface border rounded-lg shadow-lg opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 z-50">
<a href="#" class="block px-6 py-3 hover:bg-surface-container-low transition-colors duration-200 border-b">All Projects</a>
<a href="#" class="block px-6 py-3 hover:bg-surface-container-low transition-colors duration-200">Project Detail</a>
</div>
</div>
<!-- Blog Dropdown -->
<div class="relative group">
<button class="font-label-caps text-label-caps uppercase tracking-widest flex items-center gap-1 hover:text-primary transition-colors duration-200">
Blog
<span class="material-symbols-outlined text-sm transition-transform duration-200 group-hover:rotate-180">expand_more</span>
</button>
<div class="absolute top-full left-0 mt-2 w-56 bg-surface border rounded-lg shadow-lg opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 z-50">
<a href="#" class="block px-6 py-3 hover:bg-surface-container-low transition-colors duration-200 border-b">All Posts</a>
<a href="#" class="block px-6 py-3 hover:bg-surface-container-low transition-colors duration-200">Post Detail</a>
</div>
</div>
<a href="#" class="font-label-caps text-label-caps uppercase tracking-widest hover:text-primary transition-colors duration-200">About</a>
<a href="#" class="font-label-caps text-label-caps uppercase tracking-widest hover:text-primary transition-colors duration-200">Contact</a>
</nav>
<!-- Mobile Menu with Clickable Dropdowns -->
<div class="md:hidden">
<a href="#" class="mobile-menu-item block py-3 px-2 font-label-caps text-label-caps uppercase tracking-widest hover:bg-surface-container-low rounded transition-colors duration-200">Home</a>
<div class="mobile-dropdown border-b border-surface-container-highest">
<button class="mobile-dropdown-toggle w-full flex items-center justify-between py-3 px-2 font-label-caps text-label-caps uppercase tracking-widest hover:bg-surface-container-low rounded transition-colors duration-200">
<span>Portfolio</span>
<span class="material-symbols-outlined text-sm transition-transform duration-200">expand_more</span>
</button>
<div class="mobile-dropdown-menu hidden pl-4 space-y-1 pb-2">
<a href="#" class="block py-2 px-2 hover:bg-surface-container-low rounded transition-colors duration-200">All Projects</a>
<a href="#" class="block py-2 px-2 hover:bg-surface-container-low rounded transition-colors duration-200">Project Detail</a>
</div>
</div>
<div class="mobile-dropdown border-b border-surface-container-highest">
<button class="mobile-dropdown-toggle w-full flex items-center justify-between py-3 px-2 font-label-caps text-label-caps uppercase tracking-widest hover:bg-surface-container-low rounded transition-colors duration-200">
<span>Blog</span>
<span class="material-symbols-outlined text-sm transition-transform duration-200">expand_more</span>
</button>
<div class="mobile-dropdown-menu hidden pl-4 space-y-1 pb-2">
<a href="#" class="block py-2 px-2 hover:bg-surface-container-low rounded transition-colors duration-200">All Posts</a>
<a href="#" class="block py-2 px-2 hover:bg-surface-container-low rounded transition-colors duration-200">Post Detail</a>
</div>
</div>
<a href="#" class="mobile-menu-item block py-3 px-2 font-label-caps text-label-caps uppercase tracking-widest hover:bg-surface-container-low rounded transition-colors duration-200">About</a>
<a href="#" class="mobile-menu-item block py-3 px-2 font-label-caps text-label-caps uppercase tracking-widest hover:bg-surface-container-low rounded transition-colors duration-200">Contact</a>
</div>
Best Practices for Dropdown Menus
1. Keyboard Accessibility
Add keyboard support for users who navigate without a mouse. The group-focus variant can help style elements when they receive focus:
<button class="group-focus:opacity-100">Menu</button>
2. Touch-Friendly Targets
On mobile, ensure touch targets are at least 44px tall for comfortable tapping . The EXHIBIT template uses py-3 (12px padding) on mobile items.
3. Smooth Transitions
Use transition-all duration-200 or similar classes to create polished animations. The group-hover:rotate-180 class creates a satisfying rotation effect on the dropdown arrow .
4. Z-Index Management
Dropdowns should appear above other content. The z-50 class ensures your dropdowns stay on top .
5. Test Across Devices
Always test your dropdowns on actual mobile devices and tablets. The responsive behavior that works in a browser's device emulator might behave differently on real hardware.
Get the Complete Template
This tutorial is based on the EXHIBIT – Creative Portfolio HTML Template available at TemplatesJungle. The template includes:
- ✅ Fully responsive dropdown navigation
- ✅ 6+ unique pages with working demos
- ✅ Dark mode support
- ✅ Scroll progress indicator
- ✅ Working contact form with PHP backend
- ✅ Material Design icons
- ✅ Professional typography with Google Fonts
Have questions about implementing dropdowns in your project? Leave a comment below. And do check out Free TailWind CSS Templates for more ready-to-use TailWind Website Templates.
Top comments (0)