DEV Community

Cover image for How to Create a Responsive Dropdown Menu in Tailwind CSS
TemplatesJungle
TemplatesJungle

Posted on

How to Create a Responsive Dropdown Menu in Tailwind CSS

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

Key classes:

  • group-hover:rotate-180 rotates the icon when the parent is hovered
  • transition-transform duration-200 ensures 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>
Enter fullscreen mode Exit fullscreen mode

Breakdown of the classes:

  • absolute top-full left-0 positions the menu directly below the trigger
  • mt-2 adds spacing between trigger and menu
  • opacity-0 invisible hides the menu initially
  • group-hover:opacity-100 group-hover:visible reveals it on hover
  • transition-all duration-200 creates a smooth fade effect
  • z-50 ensures 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>
Enter fullscreen mode Exit fullscreen mode

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);
    });
});
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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)