Every great portfolio needs a navigation menu that works seamlessly across all devices. In this tutorial, you'll learn how to transform a standard desktop navigation into a sleek, full-screen mobile menu using Tailwind CSS—inspired by the WebDev portfolio template from TemplatesJungle.
We'll cover the essential HTML structure, Tailwind utility classes for responsive behavior, smooth slide-in animations, and the JavaScript to tie it all together. By the end, you'll have a professional, mobile-friendly menu that keeps your portfolio looking sharp on every screen size. Let's dive in.
Overview
The template implements a responsive navigation pattern that:
- Shows a horizontal navigation bar on desktop (md breakpoint and above)
- Displays a hamburger menu button on mobile
- Opens a full-screen overlay menu when the hamburger is clicked
- Uses smooth transitions and animations
HTML Structure
Here's the key HTML structure from the template:
Desktop Navigation
<header class="fixed top-0 w-full z-50 bg-surface/60 backdrop-blur-[24px] border-b border-on-surface/10">
<div class="flex justify-between items-center h-20 px-margin-mobile md:px-margin-desktop max-w-container-max mx-auto">
<!-- Logo -->
<a class="font-headline-md text-headline-md font-black text-primary tracking-tighter hover:opacity-80 transition-opacity"
href="#">WEBDEV</a>
<!-- Desktop Navigation - Hidden on mobile, visible on md+ -->
<nav class="hidden md:flex items-center gap-8">
<a class="text-primary border-b-2 border-primary pb-1 font-label-caps text-label-caps transition-all"
href="#">Projects</a>
<a class="text-on-surface-variant font-label-caps text-label-caps hover:text-primary transition-colors hover:translate-y-[-1px]"
href="#">Experience</a>
<a class="text-on-surface-variant font-label-caps text-label-caps hover:text-primary transition-colors hover:translate-y-[-1px]"
href="#">About</a>
<a class="text-on-surface-variant font-label-caps text-label-caps hover:text-primary transition-colors hover:translate-y-[-1px]"
href="#">Contact</a>
</nav>
<!-- Mobile Menu Button - Visible only on mobile -->
<button id="mobile-menu-open"
class="md:hidden flex items-center justify-center p-2 text-on-surface hover:text-primary transition-colors">
<span class="material-symbols-outlined text-3xl">menu</span>
</button>
<!-- Resume Button - Hidden on mobile -->
<button class="hidden md:flex shimmer-btn text-on-primary px-6 py-2 rounded-full font-label-caps text-label-caps hover:drop-shadow-[0_0_15px_rgba(219,252,255,0.4)] active:scale-95 transition-all duration-200">
Resume
</button>
</div>
</header>
Mobile Overlay Menu
<div id="mobile-menu-overlay"
class="fixed inset-0 z-[60] bg-background/95 backdrop-blur-xl translate-x-full transition-transform duration-500 md:hidden flex flex-col">
<!-- Overlay Header -->
<div class="flex justify-between items-center h-20 px-margin-mobile">
<a class="font-headline-md text-headline-md font-black text-primary tracking-tighter" href="#">WEBDEV</a>
<button id="mobile-menu-close" class="p-2 text-on-surface hover:text-primary transition-colors">
<span class="material-symbols-outlined text-3xl">close</span>
</button>
</div>
<!-- Mobile Navigation Links -->
<nav class="flex flex-col items-center justify-center flex-grow gap-10">
<a class="text-primary text-2xl font-headline-md" href="#">Projects</a>
<a class="text-on-surface-variant text-2xl font-headline-md hover:text-primary transition-colors" href="#">Experience</a>
<a class="text-on-surface-variant text-2xl font-headline-md hover:text-primary transition-colors" href="#">About</a>
<a class="text-on-surface-variant text-2xl font-headline-md hover:text-primary transition-colors" href="#">Contact</a>
<button class="shimmer-btn text-on-primary px-10 py-3 rounded-full font-label-caps text-label-caps mt-4">
Resume
</button>
</nav>
</div>
CSS Classes Explained
Key Tailwind Utility Classes Used:
| Class | Purpose |
|---|---|
hidden md:flex |
Hide on mobile, show as flex on medium screens and above |
md:hidden |
Show on mobile, hide on medium screens |
fixed inset-0 |
Position overlay to cover entire viewport |
z-50 / z-[60]
|
Stacking context (overlay appears above header) |
bg-background/95 backdrop-blur-xl |
Semi-transparent background with blur effect |
translate-x-full |
Initially position overlay off-screen to the right |
transition-transform duration-500 |
Smooth animation when toggling |
flex flex-col |
Vertical flex layout for overlay content |
JavaScript Interactivity
Here's the JavaScript needed to make the menu functional:
// script.js
document.addEventListener('DOMContentLoaded', function() {
const mobileMenuButton = document.getElementById('mobile-menu-open');
const mobileMenuClose = document.getElementById('mobile-menu-close');
const mobileMenuOverlay = document.getElementById('mobile-menu-overlay');
// Function to open mobile menu
function openMobileMenu() {
mobileMenuOverlay.classList.remove('translate-x-full');
document.body.style.overflow = 'hidden'; // Prevent background scrolling
}
// Function to close mobile menu
function closeMobileMenu() {
mobileMenuOverlay.classList.add('translate-x-full');
document.body.style.overflow = ''; // Restore scrolling
}
// Event listeners
mobileMenuButton.addEventListener('click', openMobileMenu);
mobileMenuClose.addEventListener('click', closeMobileMenu);
// Close menu when clicking on a navigation link
const mobileNavLinks = mobileMenuOverlay.querySelectorAll('nav a');
mobileNavLinks.forEach(link => {
link.addEventListener('click', closeMobileMenu);
});
// Close menu when pressing Escape key
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape' && !mobileMenuOverlay.classList.contains('translate-x-full')) {
closeMobileMenu();
}
});
});
Step-by-Step Implementation Guide
Step 1: Set Up Your HTML Structure
Start with a fixed header containing your logo, desktop navigation, and mobile menu button:
<header class="fixed top-0 w-full z-50 bg-surface/60 backdrop-blur-[24px] border-b border-on-surface/10">
<div class="flex justify-between items-center h-20 px-4 md:px-8 max-w-7xl mx-auto">
<!-- Logo -->
<a href="/" class="text-xl font-bold text-primary">LOGO</a>
<!-- Desktop Navigation -->
<nav class="hidden md:flex items-center gap-6">
<a href="#" class="hover:text-primary transition">Home</a>
<a href="#" class="hover:text-primary transition">About</a>
<a href="#" class="hover:text-primary transition">Services</a>
<a href="#" class="hover:text-primary transition">Contact</a>
</nav>
<!-- Mobile Menu Button -->
<button id="menu-btn" class="md:hidden p-2">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/>
</svg>
</button>
</div>
</header>
Step 2: Create the Mobile Overlay
Place this after your header but before closing the body tag:
<div id="mobile-menu" class="fixed inset-0 z-50 bg-black/95 backdrop-blur-lg transform translate-x-full transition-transform duration-300 md:hidden">
<div class="flex justify-between items-center h-20 px-4 border-b border-white/10">
<a href="/" class="text-xl font-bold text-primary">LOGO</a>
<button id="close-menu" class="p-2">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
<nav class="flex flex-col items-center justify-center h-full gap-8">
<a href="#" class="text-2xl hover:text-primary transition">Home</a>
<a href="#" class="text-2xl hover:text-primary transition">About</a>
<a href="#" class="text-2xl hover:text-primary transition">Services</a>
<a href="#" class="text-2xl hover:text-primary transition">Contact</a>
</nav>
</div>
Step 3: Add JavaScript Toggle Logic
const menuBtn = document.getElementById('menu-btn');
const closeBtn = document.getElementById('close-menu');
const mobileMenu = document.getElementById('mobile-menu');
function openMenu() {
mobileMenu.classList.remove('translate-x-full');
document.body.style.overflow = 'hidden';
}
function closeMenu() {
mobileMenu.classList.add('translate-x-full');
document.body.style.overflow = '';
}
menuBtn?.addEventListener('click', openMenu);
closeBtn?.addEventListener('click', closeMenu);
// Close menu on link click
mobileMenu?.querySelectorAll('a').forEach(link => {
link.addEventListener('click', closeMenu);
});
Customization Options
Change Animation Direction
<!-- Slide from left instead of right -->
<div class="... -translate-x-full transition-transform ...">
Change Overlay Style
<!-- Different background styles -->
<div class="... bg-surface/98 backdrop-blur-sm ...">
<div class="... bg-gradient-to-b from-background to-surface ...">
Add Active Link Highlighting
// Add active class to current page link
const currentPath = window.location.pathname;
mobileMenu.querySelectorAll('a').forEach(link => {
if (link.getAttribute('href') === currentPath) {
link.classList.add('text-primary', 'border-b-2', 'border-primary');
}
});
Best Practices
-
Always set
position: fixedon the overlay to cover the entire viewport - Disable body scroll when menu is open for better UX
- Use high z-index values (z-50 or higher) for overlays
- Include both open and close buttons for accessibility
- Test on actual mobile devices to verify touch targets (minimum 44x44px)
- Add focus management for keyboard navigation
-
Consider using
prefers-reduced-motionfor users who prefer less animation
Complete Working Example
Here's a minimal complete example combining everything:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.tailwindcss.com"></script>
<title>Responsive Menu Demo</title>
</head>
<body class="bg-gray-900 text-white">
<header class="fixed top-0 w-full z-50 bg-gray-900/80 backdrop-blur-md border-b border-white/10">
<div class="flex justify-between items-center h-16 px-4 md:px-8 max-w-6xl mx-auto">
<a href="/" class="text-xl font-bold text-cyan-400">LOGO</a>
<nav class="hidden md:flex gap-6">
<a href="#" class="hover:text-cyan-400 transition">Home</a>
<a href="#" class="hover:text-cyan-400 transition">About</a>
<a href="#" class="hover:text-cyan-400 transition">Services</a>
<a href="#" class="hover:text-cyan-400 transition">Contact</a>
</nav>
<button id="menuBtn" class="md:hidden p-2">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/>
</svg>
</button>
</div>
</header>
<div id="mobileMenu" class="fixed inset-0 z-50 bg-gray-900/95 backdrop-blur-lg transform translate-x-full transition-transform duration-300 md:hidden">
<div class="flex justify-between items-center h-16 px-4 border-b border-white/10">
<a href="/" class="text-xl font-bold text-cyan-400">LOGO</a>
<button id="closeBtn" class="p-2">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
<nav class="flex flex-col items-center justify-center h-full gap-8">
<a href="#" class="text-2xl hover:text-cyan-400 transition">Home</a>
<a href="#" class="text-2xl hover:text-cyan-400 transition">About</a>
<a href="#" class="text-2xl hover:text-cyan-400 transition">Services</a>
<a href="#" class="text-2xl hover:text-cyan-400 transition">Contact</a>
</nav>
</div>
<main class="pt-20">
<div class="max-w-6xl mx-auto px-4 py-12">
<h1 class="text-4xl font-bold mb-4">Responsive Menu Demo</h1>
<p>Resize your browser to see the mobile menu in action!</p>
</div>
</main>
<script>
const menuBtn = document.getElementById('menuBtn');
const closeBtn = document.getElementById('closeBtn');
const mobileMenu = document.getElementById('mobileMenu');
menuBtn?.addEventListener('click', () => {
mobileMenu.classList.remove('translate-x-full');
document.body.style.overflow = 'hidden';
});
closeBtn?.addEventListener('click', () => {
mobileMenu.classList.add('translate-x-full');
document.body.style.overflow = '';
});
mobileMenu?.querySelectorAll('a').forEach(link => {
link.addEventListener('click', () => {
mobileMenu.classList.add('translate-x-full');
document.body.style.overflow = '';
});
});
</script>
</body>
</html>
If you're ready to stop wrestling with complex CSS and start building a portfolio that truly reflects your skills, download the free WebDev Tailwind CSS Portfolio Template today from TemplatesJungle.
Whether you need a personal landing page, a freelance portfolio, or a launch pad for your next product, WebDev turns your skills into a stunning, highly customizable website in no time. Grab it for free—for both personal and commercial use—and start impressing clients today.
Top comments (0)