Building Forms & Modals
Building Forms & Modals
Master building accessible, beautiful forms and modal dialogs with Tailwind CSS. Learn validation patterns, input styling, modal overlays, and best practices for user interaction.
Basic Form Structure
Start with semantic HTML and Tailwind styling for form elements:
Simple Contact Form
<form class="max-w-md mx-auto p-6 bg-white rounded-lg shadow-lg">
<h2 class="text-2xl font-bold text-gray-900 mb-6">Contact Us</h2>
<!-- Name field -->
<div class="mb-4">
<label for="name" class="block text-sm font-medium text-gray-700 mb-2">
Full Name
</label>
<input
type="text"
id="name"
name="name"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="John Doe"
>
</div>
<!-- Email field -->
<div class="mb-4">
<label for="email" class="block text-sm font-medium text-gray-700 mb-2">
Email Address
</label>
<input
type="email"
id="email"
name="email"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="john@example.com"
>
</div>
<!-- Message field -->
<div class="mb-6">
<label for="message" class="block text-sm font-medium text-gray-700 mb-2">
Message
</label>
<textarea
id="message"
name="message"
rows="4"
class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent resize-none"
placeholder="Your message here..."
></textarea>
</div>
<!-- Submit button -->
<button
type="submit"
class="w-full bg-blue-600 text-white font-semibold py-3 px-6 rounded-lg hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 transition-colors"
>
Send Message
</button>
</form>
focus:outline-none with focus:ring-2 to remove the default outline and add a custom focus ring. This improves both aesthetics and accessibility.
Form Validation Styling
Add visual feedback for valid, invalid, and disabled states:
Form with Validation States
<form class="max-w-md mx-auto p-6 bg-white rounded-lg shadow-lg">
<!-- Valid input -->
<div class="mb-4">
<label for="valid-email" class="block text-sm font-medium text-gray-700 mb-2">
Email (Valid)
</label>
<div class="relative">
<input
type="email"
id="valid-email"
class="w-full px-4 py-2 pr-10 border-2 border-green-500 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500"
value="user@example.com"
>
<div class="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
<svg class="h-5 w-5 text-green-500" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"></path>
</svg>
</div>
</div>
<p class="mt-1 text-sm text-green-600">Looks good!</p>
</div>
<!-- Invalid input -->
<div class="mb-4">
<label for="invalid-email" class="block text-sm font-medium text-gray-700 mb-2">
Email (Invalid)
</label>
<div class="relative">
<input
type="email"
id="invalid-email"
class="w-full px-4 py-2 pr-10 border-2 border-red-500 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-500"
value="invalid-email"
>
<div class="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
<svg class="h-5 w-5 text-red-500" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd"></path>
</svg>
</div>
</div>
<p class="mt-1 text-sm text-red-600">Please enter a valid email address.</p>
</div>
<!-- Disabled input -->
<div class="mb-4">
<label for="disabled-input" class="block text-sm font-medium text-gray-400 mb-2">
Disabled Field
</label>
<input
type="text"
id="disabled-input"
disabled
class="w-full px-4 py-2 bg-gray-100 border border-gray-300 rounded-lg text-gray-500 cursor-not-allowed"
value="Cannot edit this"
>
</div>
</form>
pr-10 on the input to make room for the icon, and pointer-events-none on the icon to allow clicking through it.
Login Form with Remember Me
Build a complete login form with checkbox and forgot password link:
Professional Login Form
<div class="min-h-screen flex items-center justify-center bg-gray-100 py-12 px-4">
<div class="max-w-md w-full space-y-8 bg-white p-8 rounded-xl shadow-lg">
<div class="text-center">
<h2 class="text-3xl font-bold text-gray-900">Sign in to your account</h2>
<p class="mt-2 text-sm text-gray-600">
Or
<a href="#" class="font-medium text-blue-600 hover:text-blue-500">
start your 14-day free trial
</a>
</p>
</div>
<form class="mt-8 space-y-6">
<div class="space-y-4">
<!-- Email -->
<div>
<label for="login-email" class="block text-sm font-medium text-gray-700 mb-1">
Email address
</label>
<input
id="login-email"
name="email"
type="email"
required
class="appearance-none relative block w-full px-3 py-2 border border-gray-300 rounded-lg placeholder-gray-400 text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="Email address"
>
</div>
<!-- Password -->
<div>
<label for="password" class="block text-sm font-medium text-gray-700 mb-1">
Password
</label>
<input
id="password"
name="password"
type="password"
required
class="appearance-none relative block w-full px-3 py-2 border border-gray-300 rounded-lg placeholder-gray-400 text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
placeholder="Password"
>
</div>
</div>
<!-- Remember me & Forgot password -->
<div class="flex items-center justify-between">
<div class="flex items-center">
<input
id="remember-me"
name="remember-me"
type="checkbox"
class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded cursor-pointer"
>
<label for="remember-me" class="ml-2 block text-sm text-gray-900 cursor-pointer">
Remember me
</label>
</div>
<div class="text-sm">
<a href="#" class="font-medium text-blue-600 hover:text-blue-500">
Forgot your password?
</a>
</div>
</div>
<!-- Submit button -->
<button
type="submit"
class="group relative w-full flex justify-center py-3 px-4 border border-transparent text-sm font-medium rounded-lg text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
>
Sign in
</button>
<!-- Social login -->
<div class="mt-6">
<div class="relative">
<div class="absolute inset-0 flex items-center">
<div class="w-full border-t border-gray-300"></div>
</div>
<div class="relative flex justify-center text-sm">
<span class="px-2 bg-white text-gray-500">Or continue with</span>
</div>
</div>
<div class="mt-6 grid grid-cols-2 gap-3">
<button type="button" class="w-full inline-flex justify-center py-2 px-4 border border-gray-300 rounded-lg shadow-sm bg-white text-sm font-medium text-gray-500 hover:bg-gray-50">
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 0C4.477 0 0 4.484 0 10.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0110 4.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.203 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.942.359.31.678.921.678 1.856 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0020 10.017C20 4.484 15.522 0 10 0z" clip-rule="evenodd"></path>
</svg>
<span class="ml-2">GitHub</span>
</button>
<button type="button" class="w-full inline-flex justify-center py-2 px-4 border border-gray-300 rounded-lg shadow-sm bg-white text-sm font-medium text-gray-500 hover:bg-gray-50">
<svg class="w-5 h-5 text-blue-600" fill="currentColor" viewBox="0 0 20 20">
<path d="M6.29 18.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0020 3.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.073 4.073 0 01.8 7.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 010 16.407a11.616 11.616 0 006.29 1.84"></path>
</svg>
<span class="ml-2">Twitter</span>
</button>
</div>
</div>
</form>
</div>
</div>
Floating Label Pattern
Create modern floating label inputs:
Floating Label Inputs
<form class="max-w-md mx-auto p-6 space-y-6">
<!-- Floating label input -->
<div class="relative">
<input
type="text"
id="floating-name"
class="block px-4 pb-2.5 pt-5 w-full text-sm text-gray-900 bg-white border border-gray-300 rounded-lg appearance-none focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent peer"
placeholder=" "
>
<label
for="floating-name"
class="absolute text-sm text-gray-500 duration-300 transform -translate-y-4 scale-75 top-4 z-10 origin-[0] left-4 peer-placeholder-shown:scale-100 peer-placeholder-shown:translate-y-0 peer-focus:scale-75 peer-focus:-translate-y-4 peer-focus:text-blue-600"
>
Full Name
</label>
</div>
<!-- Floating label email -->
<div class="relative">
<input
type="email"
id="floating-email"
class="block px-4 pb-2.5 pt-5 w-full text-sm text-gray-900 bg-white border border-gray-300 rounded-lg appearance-none focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent peer"
placeholder=" "
>
<label
for="floating-email"
class="absolute text-sm text-gray-500 duration-300 transform -translate-y-4 scale-75 top-4 z-10 origin-[0] left-4 peer-placeholder-shown:scale-100 peer-placeholder-shown:translate-y-0 peer-focus:scale-75 peer-focus:-translate-y-4 peer-focus:text-blue-600"
>
Email Address
</label>
</div>
<!-- Floating label textarea -->
<div class="relative">
<textarea
id="floating-message"
rows="4"
class="block px-4 pb-2.5 pt-5 w-full text-sm text-gray-900 bg-white border border-gray-300 rounded-lg appearance-none focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent peer resize-none"
placeholder=" "
></textarea>
<label
for="floating-message"
class="absolute text-sm text-gray-500 duration-300 transform -translate-y-4 scale-75 top-4 z-10 origin-[0] left-4 peer-placeholder-shown:scale-100 peer-placeholder-shown:translate-y-0 peer-focus:scale-75 peer-focus:-translate-y-4 peer-focus:text-blue-600"
>
Message
</label>
</div>
</form>
peer class on the input and peer-* modifiers on the label create the floating effect. The placeholder must be a space (" ") for the peer-placeholder-shown state to work correctly.
Input Groups with Icons
Add icons and buttons to input fields:
Input Groups
<form class="max-w-md mx-auto p-6 space-y-4">
<!-- Input with left icon -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Email</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<svg class="h-5 w-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 12a4 4 0 10-8 0 4 4 0 008 0zm0 0v1.5a2.5 2.5 0 005 0V12a9 9 0 10-9 9m4.5-1.206a8.959 8.959 0 01-4.5 1.207"></path>
</svg>
</div>
<input
type="email"
class="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="you@example.com"
>
</div>
</div>
<!-- Input with right button -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Website</label>
<div class="flex rounded-lg shadow-sm">
<span class="inline-flex items-center px-3 rounded-l-lg border border-r-0 border-gray-300 bg-gray-50 text-gray-500 text-sm">
https://
</span>
<input
type="text"
class="flex-1 min-w-0 block w-full px-3 py-2 border border-gray-300 rounded-none focus:ring-blue-500 focus:border-blue-500"
placeholder="www.example.com"
>
<button
type="button"
class="inline-flex items-center px-4 rounded-r-lg border border-l-0 border-gray-300 bg-gray-50 text-gray-700 hover:bg-gray-100 text-sm"
>
Copy
</button>
</div>
</div>
<!-- Password with toggle visibility -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Password</label>
<div class="relative">
<input
type="password"
id="password-toggle"
class="block w-full px-3 py-2 pr-10 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Enter password"
>
<button
type="button"
onclick="togglePassword()"
class="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-gray-600"
>
<svg id="eye-open" class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"></path>
</svg>
<svg id="eye-closed" class="h-5 w-5 hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13.875 18.825A10.05 10.05 0 0112 19c-4.478 0-8.268-2.943-9.543-7a9.97 9.97 0 011.563-3.029m5.858.908a3 3 0 114.243 4.243M9.878 9.878l4.242 4.242M9.88 9.88l-3.29-3.29m7.532 7.532l3.29 3.29M3 3l3.59 3.59m0 0A9.953 9.953 0 0112 5c4.478 0 8.268 2.943 9.543 7a10.025 10.025 0 01-4.132 5.411m0 0L21 21"></path>
</svg>
</button>
</div>
</div>
</form>
<script>
function togglePassword() {
const input = document.getElementById('password-toggle');
const eyeOpen = document.getElementById('eye-open');
const eyeClosed = document.getElementById('eye-closed');
if (input.type === 'password') {
input.type = 'text';
eyeOpen.classList.add('hidden');
eyeClosed.classList.remove('hidden');
} else {
input.type = 'password';
eyeOpen.classList.remove('hidden');
eyeClosed.classList.add('hidden');
}
}
</script>
Search Bar Component
Build various search bar designs:
Search Bars
<!-- Simple search -->
<div class="relative max-w-md">
<input
type="search"
class="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Search..."
>
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<svg class="h-5 w-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
</svg>
</div>
</div>
<!-- Search with button -->
<div class="flex max-w-md">
<input
type="search"
class="flex-1 px-4 py-2 border border-r-0 border-gray-300 rounded-l-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Search products..."
>
<button class="px-6 bg-blue-600 text-white rounded-r-lg hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500">
Search
</button>
</div>
<!-- Search with dropdown filter -->
<div class="flex max-w-2xl">
<select class="px-4 py-2 border border-r-0 border-gray-300 rounded-l-lg focus:outline-none focus:ring-2 focus:ring-blue-500 bg-white">
<option>All</option>
<option>Products</option>
<option>Articles</option>
<option>Users</option>
</select>
<input
type="search"
class="flex-1 px-4 py-2 border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="Type to search..."
>
<button class="px-6 bg-blue-600 text-white rounded-r-lg hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path>
</svg>
</button>
</div>
Modal Dialog Overlay
Build accessible modal dialogs with backdrop:
Basic Modal
<!-- Modal trigger button -->
<button
onclick="openModal()"
class="px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700"
>
Open Modal
</button>
<!-- Modal -->
<div
id="modal"
class="hidden fixed inset-0 z-50 overflow-y-auto"
aria-labelledby="modal-title"
role="dialog"
aria-modal="true"
>
<!-- Backdrop -->
<div class="fixed inset-0 bg-black bg-opacity-50 transition-opacity"></div>
<!-- Modal container -->
<div class="flex min-h-screen items-center justify-center p-4">
<!-- Modal content -->
<div class="relative bg-white rounded-lg shadow-xl max-w-md w-full transform transition-all">
<!-- Close button -->
<button
onclick="closeModal()"
class="absolute top-4 right-4 text-gray-400 hover:text-gray-600"
>
<svg class="h-6 w-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"></path>
</svg>
</button>
<!-- Modal header -->
<div class="p-6 pb-4">
<h3 id="modal-title" class="text-2xl font-bold text-gray-900">
Modal Title
</h3>
</div>
<!-- Modal body -->
<div class="px-6 pb-6">
<p class="text-gray-600 mb-4">
This is a simple modal dialog. You can add any content here like forms, images, or text.
</p>
<!-- Action buttons -->
<div class="flex gap-3 justify-end">
<button
onclick="closeModal()"
class="px-4 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50"
>
Cancel
</button>
<button class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
Confirm
</button>
</div>
</div>
</div>
</div>
</div>
<script>
function openModal() {
document.getElementById('modal').classList.remove('hidden');
document.body.style.overflow = 'hidden'; // Prevent body scroll
}
function closeModal() {
document.getElementById('modal').classList.add('hidden');
document.body.style.overflow = ''; // Restore body scroll
}
// Close on backdrop click
document.getElementById('modal').addEventListener('click', function(e) {
if (e.target === this) {
closeModal();
}
});
// Close on Escape key
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
closeModal();
}
});
</script>
overflow: hidden) and restore it when closed. This prevents scrolling the page behind the modal.
Slide-Over Panel
Create side panels that slide in from the edge:
Slide-Over from Right
<button onclick="openSlideOver()" class="px-6 py-3 bg-blue-600 text-white rounded-lg">
Open Slide-Over
</button>
<!-- Slide-over panel -->
<div id="slide-over" class="hidden fixed inset-0 z-50 overflow-hidden">
<!-- Backdrop -->
<div
onclick="closeSlideOver()"
class="fixed inset-0 bg-black bg-opacity-50 transition-opacity"
></div>
<!-- Panel container -->
<div class="fixed inset-y-0 right-0 flex max-w-full pl-10">
<!-- Panel -->
<div class="w-screen max-w-md transform transition-transform">
<div class="flex h-full flex-col bg-white shadow-xl">
<!-- Header -->
<div class="px-6 py-6 bg-blue-600">
<div class="flex items-center justify-between">
<h2 class="text-xl font-semibold text-white">Panel Title</h2>
<button onclick="closeSlideOver()" class="text-white hover:text-gray-200">
<svg class="h-6 w-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"></path>
</svg>
</button>
</div>
</div>
<!-- Body -->
<div class="flex-1 overflow-y-auto px-6 py-6">
<p class="text-gray-600 mb-4">
This is a slide-over panel. It slides in from the right side of the screen.
</p>
<!-- Add your content here -->
</div>
<!-- Footer -->
<div class="border-t border-gray-200 px-6 py-4">
<div class="flex gap-3 justify-end">
<button onclick="closeSlideOver()" class="px-4 py-2 border border-gray-300 rounded-lg">
Cancel
</button>
<button class="px-4 py-2 bg-blue-600 text-white rounded-lg">
Save
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
function openSlideOver() {
const panel = document.getElementById('slide-over');
panel.classList.remove('hidden');
// Add animation after element is visible
setTimeout(() => {
panel.querySelector('.transform').classList.remove('translate-x-full');
}, 10);
}
function closeSlideOver() {
const panel = document.getElementById('slide-over');
panel.querySelector('.transform').classList.add('translate-x-full');
setTimeout(() => {
panel.classList.add('hidden');
}, 300);
}
</script>
Drawer Navigation
Mobile-style drawer that slides from the left:
Mobile Drawer Menu
<button onclick="openDrawer()" class="px-4 py-2 bg-gray-800 text-white rounded-lg">
Open Drawer
</button>
<div id="drawer" class="hidden fixed inset-0 z-50 overflow-hidden">
<!-- Backdrop -->
<div
onclick="closeDrawer()"
class="fixed inset-0 bg-black bg-opacity-50 transition-opacity"
></div>
<!-- Drawer -->
<div class="fixed inset-y-0 left-0 max-w-xs w-full bg-white shadow-xl transform -translate-x-full transition-transform duration-300" id="drawer-content">
<!-- Header -->
<div class="flex items-center justify-between p-6 border-b">
<h2 class="text-xl font-bold text-gray-900">Menu</h2>
<button onclick="closeDrawer()" class="text-gray-400 hover:text-gray-600">
<svg class="h-6 w-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"></path>
</svg>
</button>
</div>
<!-- Navigation -->
<nav class="p-6 space-y-2">
<a href="#" class="block px-4 py-3 rounded-lg text-gray-700 hover:bg-gray-100">Home</a>
<a href="#" class="block px-4 py-3 rounded-lg text-gray-700 hover:bg-gray-100">About</a>
<a href="#" class="block px-4 py-3 rounded-lg text-gray-700 hover:bg-gray-100">Services</a>
<a href="#" class="block px-4 py-3 rounded-lg text-gray-700 hover:bg-gray-100">Contact</a>
</nav>
</div>
</div>
<script>
function openDrawer() {
document.getElementById('drawer').classList.remove('hidden');
setTimeout(() => {
document.getElementById('drawer-content').classList.remove('-translate-x-full');
}, 10);
}
function closeDrawer() {
document.getElementById('drawer-content').classList.add('-translate-x-full');
setTimeout(() => {
document.getElementById('drawer').classList.add('hidden');
}, 300);
}
</script>
Modal with Backdrop Blur
Create modern modals with blurred backgrounds:
Backdrop Blur Modal
<button onclick="openBlurModal()" class="px-6 py-3 bg-purple-600 text-white rounded-lg">
Open Blur Modal
</button>
<div id="blur-modal" class="hidden fixed inset-0 z-50">
<!-- Blurred backdrop -->
<div class="fixed inset-0 bg-black/30 backdrop-blur-sm"></div>
<!-- Modal -->
<div class="flex min-h-screen items-center justify-center p-4">
<div class="relative bg-white/90 backdrop-blur-md rounded-2xl shadow-2xl max-w-lg w-full p-8">
<button
onclick="closeBlurModal()"
class="absolute top-4 right-4 text-gray-400 hover:text-gray-600"
>
<svg class="h-6 w-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"></path>
</svg>
</button>
<h3 class="text-2xl font-bold text-gray-900 mb-4">
Glassmorphism Modal
</h3>
<p class="text-gray-600 mb-6">
This modal uses backdrop-blur for a modern glassmorphism effect. The background is slightly blurred and semi-transparent.
</p>
<div class="flex gap-3 justify-end">
<button onclick="closeBlurModal()" class="px-4 py-2 border border-gray-300 rounded-lg">
Cancel
</button>
<button class="px-4 py-2 bg-purple-600 text-white rounded-lg">
Continue
</button>
</div>
</div>
</div>
</div>
<script>
function openBlurModal() {
document.getElementById('blur-modal').classList.remove('hidden');
}
function closeBlurModal() {
document.getElementById('blur-modal').classList.add('hidden');
}
</script>
backdrop-blur-sm utility creates a blur effect on elements behind the modal. Combine with semi-transparent backgrounds (bg-black/30) for glassmorphism effects.
Exercise 1: Registration Form
Build a complete multi-step registration form:
- Step 1: Email and password with validation
- Step 2: Name, phone, date of birth
- Step 3: Address and preferences
- Progress indicator showing current step
- Next/Previous/Submit buttons with proper states
- Validation errors for each field
Exercise 2: Confirmation Modal
Create a reusable confirmation modal component:
- Warning icon with different variants (info, warning, danger, success)
- Title and description props
- Cancel and Confirm buttons with customizable text and colors
- Backdrop click and Escape key to close
- Smooth fade-in/fade-out animation
- Focus trap (keyboard navigation stays inside modal)
Exercise 3: Shopping Cart Slide-Over
Build a shopping cart panel that slides from the right:
- List of cart items with image, name, price, quantity controls
- Remove item button for each product
- Subtotal, tax, and total calculation
- Checkout button in footer
- Empty cart state with illustration
- Smooth slide-in animation from right edge