Tailwind CSS Modals Collection
Explore this collection of responsive modal dialogs built with Tailwind CSS. These components provide versatile solutions for user interactions, notifications, forms, and more, designed for easy integration into your projects.
Each modal comes with clean HTML structure and utility classes. Use the "Preview" tab to see them in action (you'll need JavaScript to trigger them in your own project) and the "HTML" tab to copy the code directly.
Standard Modal
Standard Modal Title
This is the content area for the standard modal. You can put any text, images, or other elements here. Keep it concise and informative.
<!-- Standard Modal -->
<!-- Add JavaScript to handle the 'hidden' class toggling -->
<!-- Trigger Button (Example - Not part of the modal structure itself) -->
<!-- <button onclick="document.getElementById('my-standard-modal').classList.remove('hidden')">Open Modal</button> -->
<!-- Modal Container (Initially hidden) -->
<div id="my-standard-modal" class="fixed inset-0 z-50 hidden flex items-center justify-center p-4 bg-black bg-opacity-50">
<!-- Modal Content -->
<div class="bg-white rounded-lg shadow-xl max-w-md w-full mx-auto overflow-hidden" role="dialog" aria-modal="true" aria-labelledby="standard-modal-title" aria-describedby="standard-modal-description">
<!-- Modal Header -->
<div class="flex justify-between items-center p-4 border-b border-gray-200">
<h3 class="text-lg font-semibold text-gray-800" id="standard-modal-title">Standard Modal Title</h3>
<!-- Close Button -->
<button onclick="document.getElementById('my-standard-modal').classList.add('hidden')" class="text-gray-400 hover:text-gray-600" aria-label="Close modal">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
</button>
</div>
<!-- Modal Body -->
<div class="p-6">
<p class="text-gray-600 text-sm" id="standard-modal-description">
This is the content area for the standard modal. You can put any text, images, or other elements here. Keep it concise and informative.
</p>
</div>
<!-- Modal Footer -->
<div class="flex justify-end items-center p-4 bg-gray-50 border-t border-gray-200 space-x-2">
<!-- Cancel Button -->
<button onclick="document.getElementById('my-standard-modal').classList.add('hidden')" class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
Cancel
</button>
<!-- Confirm Button -->
<button class="px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
Confirm Action
</button>
</div>
</div> <!-- End Modal Content -->
</div> <!-- End Modal Container -->
Centered Modal
Action Successful
Your changes have been saved successfully. You can continue using the application.
<!-- Centered Modal -->
<!-- Add JavaScript to handle the 'hidden' class toggling -->
<!-- Trigger Button (Example) -->
<!-- <button onclick="document.getElementById('my-centered-modal').classList.remove('hidden')">Open Modal</button> -->
<!-- Modal Container (Fixed position, centered items) -->
<div id="my-centered-modal" class="fixed inset-0 z-50 hidden flex items-center justify-center p-4 bg-black bg-opacity-50">
<!-- Modal Content (Centered, max-width, relative for close button) -->
<div class="bg-white rounded-lg shadow-xl max-w-lg w-full mx-auto text-center p-6 relative" role="dialog" aria-modal="true" aria-labelledby="centered-modal-title">
<!-- Close button (Absolute positioned top-right) -->
<button onclick="document.getElementById('my-centered-modal').classList.add('hidden')" class="absolute top-3 right-3 text-gray-400 hover:text-gray-600" aria-label="Close modal">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
</button>
<!-- Optional Icon -->
<div class="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-green-100 mb-4">
<!-- Success Icon Example -->
<svg class="h-6 w-6 text-green-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5" />
</svg>
</div>
<!-- Modal Title -->
<h3 class="text-lg font-medium leading-6 text-gray-900 mb-2" id="centered-modal-title">Action Successful</h3>
<!-- Modal Body -->
<div class="mt-2 px-7 py-3">
<p class="text-sm text-gray-500">
Your changes have been saved successfully. You can continue using the application.
</p>
</div>
<!-- Modal Footer/Actions -->
<div class="mt-4">
<button onclick="document.getElementById('my-centered-modal').classList.add('hidden')" class="inline-flex justify-center rounded-md border border-transparent bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2">
Got it, thanks!
</button>
</div>
</div> <!-- End Modal Content -->
</div> <!-- End Modal Container -->
Fullscreen Modal
Fullscreen Content Editor
Document Title
This area takes up the full screen (minus the header/footer). It's ideal for immersive tasks like editing content, viewing large images, or complex forms.
You can place any content here. Use Tailwind's utility classes for layout and styling as needed.
More content down here to demonstrate scrolling...
<!-- Fullscreen Modal -->
<!-- Add JavaScript to handle the 'hidden' class toggling -->
<!-- Trigger Button (Example) -->
<!-- <button onclick="document.getElementById('my-fullscreen-modal').classList.remove('hidden')">Open Fullscreen Modal</button> -->
<!-- Modal Container (Fixed, full screen, flex column) -->
<div id="my-fullscreen-modal" class="fixed inset-0 z-50 hidden flex flex-col bg-gray-50" role="dialog" aria-modal="true" aria-labelledby="fullscreen-modal-title" aria-describedby="fullscreen-modal-description">
<!-- Modal Header -->
<div class="flex justify-between items-center p-4 border-b border-gray-200 bg-white w-full flex-shrink-0">
<h3 class="text-lg font-semibold text-gray-800" id="fullscreen-modal-title">Fullscreen Content Editor</h3>
<!-- Close Button -->
<button onclick="document.getElementById('my-fullscreen-modal').classList.add('hidden')" class="text-gray-500 hover:text-gray-700" aria-label="Close modal">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
</button>
</div>
<!-- Modal Body (Flex grow, scrollable) -->
<div class="flex-grow overflow-y-auto p-6 bg-gray-50 w-full" id="fullscreen-modal-description">
<!-- Your fullscreen content goes here -->
<h4 class="text-xl font-semibold mb-4">Document Title</h4>
<p class="mb-4">This area takes up the full screen (minus the header/footer). It's ideal for immersive tasks like editing content, viewing large images, or complex forms.</p>
<img src="https://picsum.photos/800/400?random=1" alt="Placeholder image" class="rounded shadow mb-4 w-full object-cover">
<p class="mb-4">You can place any content here. Use Tailwind's utility classes for layout and styling as needed.</p>
<textarea class="w-full h-40 border border-gray-300 rounded p-2 focus:ring-blue-500 focus:border-blue-500" placeholder="Start writing your content here..."></textarea>
<!-- Add more content to enable scrolling -->
<div class="h-96"></div> <!-- Spacer for demo -->
<p>End of content.</p>
</div>
<!-- Modal Footer -->
<div class="flex justify-end items-center p-4 bg-white border-t border-gray-200 w-full flex-shrink-0 space-x-2">
<!-- Discard Button -->
<button onclick="document.getElementById('my-fullscreen-modal').classList.add('hidden')" class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
Discard Changes
</button>
<!-- Save Button -->
<button class="px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
Save Content
</button>
</div>
</div> <!-- End Modal Container -->
Login / Signup Modal
WPDean
Login or create an account
Don't have an account? Sign up
<!-- Login / Signup Modal -->
<!-- Add JavaScript to handle the 'hidden' class toggling -->
<!-- Trigger Button (Example) -->
<!-- <button onclick="document.getElementById('my-login-modal').classList.remove('hidden')">Open Login Modal</button> -->
<!-- Modal Container -->
<div id="my-login-modal" class="fixed inset-0 z-50 hidden flex items-center justify-center p-4 bg-black bg-opacity-50">
<!-- Modal Content (relative for close button) -->
<div class="bg-white rounded-lg shadow-xl max-w-md w-full mx-auto overflow-hidden relative" role="dialog" aria-modal="true" aria-labelledby="login-modal-title">
<!-- Close Button (Absolute positioned top-right) -->
<button onclick="document.getElementById('my-login-modal').classList.add('hidden')" class="absolute top-3 right-3 text-gray-400 hover:text-gray-600 text-2xl leading-none z-10" aria-label="Close">×</button>
<!-- Form Content Area -->
<div class="p-8">
<!-- Logo/Header -->
<div class="text-center mb-6">
<h2 id="login-modal-title" class="text-2xl font-bold text-blue-600">WPDean</h2> <!-- Logo text -->
<p class="text-gray-600 mt-1">Login or create an account</p>
</div>
<!-- Login Form -->
<form action="#" method="POST" class="space-y-4">
<div>
<label for="login-modal-email" class="sr-only">Email address</label>
<input id="login-modal-email" name="email" type="email" autocomplete="email" required class="appearance-none rounded-md relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm" placeholder="Email address">
</div>
<div>
<label for="login-modal-password" class="sr-only">Password</label>
<input id="login-modal-password" name="password" type="password" autocomplete="current-password" required class="appearance-none rounded-md relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm" placeholder="Password">
</div>
<!-- Remember Me & Forgot Password -->
<div class="flex items-center justify-between text-sm">
<div class="flex items-center">
<input id="login-modal-remember-me" name="remember-me" type="checkbox" class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded">
<label for="login-modal-remember-me" class="ml-2 block text-gray-900"> Remember me </label>
</div>
<div class="font-medium">
<a href="#" class="text-indigo-600 hover:text-indigo-500"> Forgot your password? </a>
</div>
</div>
<!-- Submit Button -->
<div>
<button type="submit" class="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
Sign in
</button>
</div>
</form>
<!-- Signup Link -->
<p class="mt-6 text-center text-sm text-gray-600">
Don't have an account?
<a href="#" class="font-medium text-indigo-600 hover:text-indigo-500"> Sign up </a>
</p>
</div> <!-- End Form Content Area -->
</div> <!-- End Modal Content -->
</div> <!-- End Modal Container -->
Confirmation Modal
Delete Item
Are you sure you want to delete this item? This action cannot be undone.
<!-- Confirmation Modal -->
<!-- Add JavaScript to handle the 'hidden' class toggling -->
<!-- Trigger Button (Example) -->
<!-- <button onclick="document.getElementById('my-confirm-modal').classList.remove('hidden')">Open Confirmation</button> -->
<!-- Modal Container -->
<div id="my-confirm-modal" class="fixed inset-0 z-50 hidden flex items-center justify-center p-4 bg-black bg-opacity-50">
<!-- Modal Content -->
<div class="bg-white rounded-lg shadow-xl max-w-md w-full mx-auto" role="dialog" aria-modal="true" aria-labelledby="confirm-modal-title" aria-describedby="confirm-modal-description">
<!-- Modal Body (with icon and text) -->
<div class="flex items-start p-6">
<!-- Icon Container -->
<div class="flex-shrink-0 flex items-center justify-center h-12 w-12 rounded-full bg-red-100 sm:mx-0 sm:h-10 sm:w-10">
<!-- Exclamation Icon -->
<svg class="h-6 w-6 text-red-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
</svg>
</div>
<!-- Text Content -->
<div class="ml-4 text-left">
<h3 class="text-lg leading-6 font-medium text-gray-900" id="confirm-modal-title">Delete Item</h3>
<div class="mt-2">
<p class="text-sm text-gray-500" id="confirm-modal-description">
Are you sure you want to delete this item? This action cannot be undone.
</p>
</div>
</div>
</div>
<!-- Modal Footer (with action buttons) -->
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse rounded-b-lg">
<!-- Confirmation Button (e.g., Delete) -->
<button type="button" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-red-600 text-base font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500 sm:ml-3 sm:w-auto sm:text-sm">
Delete
</button>
<!-- Cancel Button -->
<button type="button" onclick="document.getElementById('my-confirm-modal').classList.add('hidden')" class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm">
Cancel
</button>
</div>
</div> <!-- End Modal Content -->
</div> <!-- End Modal Container -->
Alert Modal (Generic)
Alert
This is a simple alert message to inform the user about something important.
<!-- Alert Modal -->
<!-- Add JavaScript to handle the 'hidden' class toggling -->
<!-- Trigger Button (Example) -->
<!-- <button onclick="document.getElementById('my-alert-modal').classList.remove('hidden')">Show Alert</button> -->
<!-- Modal Container -->
<div id="my-alert-modal" class="fixed inset-0 z-50 hidden flex items-center justify-center p-4 bg-black bg-opacity-50">
<!-- Modal Content -->
<div class="bg-white rounded-lg shadow-xl max-w-sm w-full mx-auto p-6 text-center" role="alertdialog" aria-modal="true" aria-labelledby="alert-modal-title" aria-describedby="alert-modal-description">
<!-- Modal Title -->
<h3 class="text-lg font-medium text-gray-900" id="alert-modal-title">Alert</h3>
<!-- Modal Description -->
<p class="mt-2 text-sm text-gray-600" id="alert-modal-description">
This is a simple alert message to inform the user about something important.
</p>
<!-- OK Button -->
<div class="mt-4">
<button onclick="document.getElementById('my-alert-modal').classList.add('hidden')" class="px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
OK
</button>
</div>
</div> <!-- End Modal Content -->
</div> <!-- End Modal Container -->
Success Modal
Success!
Your action was completed successfully.
<!-- Success Modal -->
<!-- Add JavaScript to handle the 'hidden' class toggling -->
<!-- Trigger Button (Example) -->
<!-- <button onclick="document.getElementById('my-success-modal').classList.remove('hidden')">Show Success</button> -->
<!-- Modal Container -->
<div id="my-success-modal" class="fixed inset-0 z-50 hidden flex items-center justify-center p-4 bg-black bg-opacity-50">
<!-- Modal Content -->
<div class="bg-white rounded-lg shadow-xl max-w-sm w-full mx-auto p-6 text-center" role="alertdialog" aria-modal="true" aria-labelledby="success-modal-title" aria-describedby="success-modal-description">
<!-- Icon -->
<div class="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-green-100 mb-4">
<svg class="h-6 w-6 text-green-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5" />
</svg>
</div>
<!-- Title -->
<h3 class="text-lg font-medium text-gray-900" id="success-modal-title">Success!</h3>
<!-- Description -->
<p class="mt-2 text-sm text-gray-600" id="success-modal-description">
Your action was completed successfully.
</p>
<!-- Close Button -->
<div class="mt-4">
<button onclick="document.getElementById('my-success-modal').classList.add('hidden')" class="px-4 py-2 text-sm font-medium text-white bg-green-600 border border-transparent rounded-md hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500">
Great!
</button>
</div>
</div> <!-- End Modal Content -->
</div> <!-- End Modal Container -->
Error Modal
Error
Something went wrong. Please try again later.
<!-- Error Modal -->
<!-- Add JavaScript to handle the 'hidden' class toggling -->
<!-- Trigger Button (Example) -->
<!-- <button onclick="document.getElementById('my-error-modal').classList.remove('hidden')">Show Error</button> -->
<!-- Modal Container -->
<div id="my-error-modal" class="fixed inset-0 z-50 hidden flex items-center justify-center p-4 bg-black bg-opacity-50">
<!-- Modal Content -->
<div class="bg-white rounded-lg shadow-xl max-w-sm w-full mx-auto p-6 text-center" role="alertdialog" aria-modal="true" aria-labelledby="error-modal-title" aria-describedby="error-modal-description">
<!-- Icon -->
<div class="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-red-100 mb-4">
<svg class="h-6 w-6 text-red-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
</div>
<!-- Title -->
<h3 class="text-lg font-medium text-gray-900" id="error-modal-title">Error</h3>
<!-- Description -->
<p class="mt-2 text-sm text-gray-600" id="error-modal-description">
Something went wrong. Please try again later.
</p>
<!-- Close Button -->
<div class="mt-4">
<button onclick="document.getElementById('my-error-modal').classList.add('hidden')" class="px-4 py-2 text-sm font-medium text-white bg-red-600 border border-transparent rounded-md hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500">
Close
</button>
</div>
</div> <!-- End Modal Content -->
</div> <!-- End Modal Container -->
Warning Modal
Warning
Proceeding may have unintended consequences. Are you sure?
<!-- Warning Modal -->
<!-- Add JavaScript to handle the 'hidden' class toggling -->
<!-- Trigger Button (Example) -->
<!-- <button onclick="document.getElementById('my-warning-modal').classList.remove('hidden')">Show Warning</button> -->
<!-- Modal Container -->
<div id="my-warning-modal" class="fixed inset-0 z-50 hidden flex items-center justify-center p-4 bg-black bg-opacity-50">
<!-- Modal Content -->
<div class="bg-white rounded-lg shadow-xl max-w-sm w-full mx-auto p-6 text-center" role="alertdialog" aria-modal="true" aria-labelledby="warning-modal-title" aria-describedby="warning-modal-description">
<!-- Icon -->
<div class="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-yellow-100 mb-4">
<svg class="h-6 w-6 text-yellow-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" />
</svg>
</div>
<!-- Title -->
<h3 class="text-lg font-medium text-gray-900" id="warning-modal-title">Warning</h3>
<!-- Description -->
<p class="mt-2 text-sm text-gray-600" id="warning-modal-description">
Proceeding may have unintended consequences. Are you sure?
</p>
<!-- Action Buttons -->
<div class="mt-4 space-x-2">
<!-- Cancel Button -->
<button onclick="document.getElementById('my-warning-modal').classList.add('hidden')" class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
Cancel
</button>
<!-- Proceed Button -->
<button class="px-4 py-2 text-sm font-medium text-white bg-yellow-500 border border-transparent rounded-md hover:bg-yellow-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-yellow-400">
Proceed Anyway
</button>
</div>
</div> <!-- End Modal Content -->
</div> <!-- End Modal Container -->
Info Modal
Information
Here's some helpful information you might need to know.
<!-- Info Modal -->
<!-- Add JavaScript to handle the 'hidden' class toggling -->
<!-- Trigger Button (Example) -->
<!-- <button onclick="document.getElementById('my-info-modal').classList.remove('hidden')">Show Info</button> -->
<!-- Modal Container -->
<div id="my-info-modal" class="fixed inset-0 z-50 hidden flex items-center justify-center p-4 bg-black bg-opacity-50">
<!-- Modal Content -->
<div class="bg-white rounded-lg shadow-xl max-w-sm w-full mx-auto p-6 text-center" role="alertdialog" aria-modal="true" aria-labelledby="info-modal-title" aria-describedby="info-modal-description">
<!-- Icon -->
<div class="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-blue-100 mb-4">
<svg class="h-6 w-6 text-blue-600" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z" />
</svg>
</div>
<!-- Title -->
<h3 class="text-lg font-medium text-gray-900" id="info-modal-title">Information</h3>
<!-- Description -->
<p class="mt-2 text-sm text-gray-600" id="info-modal-description">
Here's some helpful information you might need to know.
</p>
<!-- Close Button -->
<div class="mt-4">
<button onclick="document.getElementById('my-info-modal').classList.add('hidden')" class="px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
Got it
</button>
</div>
</div> <!-- End Modal Content -->
</div> <!-- End Modal Container -->
Image/Lightbox Modal
Image Preview
<!-- Trigger Button -->
<button id="openImageModalBtn" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
View Image
</button>
<!-- Image Modal -->
<div id="imageModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50 hidden flex items-center justify-center"> <!-- Added flex centering -->
<div class="relative mx-auto p-5 border w-11/12 md:w-1/2 lg:w-1/3 shadow-lg rounded-md bg-white"> <!-- Removed top-1/4 -->
<!-- Modal Header -->
<div class="flex justify-between items-center pb-3">
<p class="text-2xl font-bold">Image Viewer</p>
<button id="closeImageModalBtn" class="text-gray-400 hover:text-gray-600 text-3xl font-light focus:outline-none">×</button>
</div>
<!-- Modal Body -->
<div class="my-5">
<img src="https://picsum.photos/400/300" alt="Modal Image" class="w-full h-auto rounded shadow">
</div>
<!-- Optional Footer for controls/caption -->
<div class="flex justify-end pt-2">
<button id="closeImageModalBtnFooter" class="px-4 bg-gray-200 p-3 rounded-lg text-gray-600 hover:bg-gray-300">Close</button>
</div>
</div>
</div>
<script>
// Get modal element
const imageModal = document.getElementById('imageModal');
// Get open modal button
const openImageModalBtn = document.getElementById('openImageModalBtn');
// Get close modal button
const closeImageModalBtn = document.getElementById('closeImageModalBtn');
// Get close modal button in footer (optional)
const closeImageModalBtnFooter = document.getElementById('closeImageModalBtnFooter');
// Function to open modal
const openImageModal = () => {
imageModal.classList.remove('hidden');
document.body.style.overflow = 'hidden'; // Prevent background scrolling
}
// Function to close modal
const closeImageModal = () => {
imageModal.classList.add('hidden');
document.body.style.overflow = ''; // Restore background scrolling
}
// Event listeners
openImageModalBtn.addEventListener('click', openImageModal);
closeImageModalBtn.addEventListener('click', closeImageModal);
if (closeImageModalBtnFooter) { // Check if footer button exists
closeImageModalBtnFooter.addEventListener('click', closeImageModal);
}
// Close modal if background overlay is clicked
imageModal.addEventListener('click', (event) => {
// Check if the click is on the overlay itself (the flex container)
if (event.target === imageModal) {
closeImageModal();
}
});
// Close modal with Escape key
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && !imageModal.classList.contains('hidden')) {
closeImageModal();
}
});
</script>
Gallery Modal
Image Gallery
Basic gallery display. More complex interactions (like clicking thumbnails to enlarge) would require additional JavaScript.
<!-- Trigger Button -->
<button id="openGalleryModalBtn" class="bg-purple-500 hover:bg-purple-700 text-white font-bold py-2 px-4 rounded">
Open Gallery
</button>
<!-- Gallery Modal -->
<div id="galleryModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50 hidden flex items-center justify-center"> <!-- Added flex centering -->
<div class="relative mx-auto p-5 border w-11/12 md:w-3/4 lg:w-2/3 shadow-lg rounded-md bg-white"> <!-- Removed top-10 -->
<!-- Modal Header -->
<div class="flex justify-between items-center pb-3 border-b border-gray-200">
<p class="text-2xl font-bold text-gray-700">Image Gallery</p>
<button id="closeGalleryModalBtn" class="text-gray-400 hover:text-gray-600 text-3xl font-light focus:outline-none">×</button>
</div>
<!-- Modal Body -->
<div class="my-5">
<p class="text-gray-600 text-sm mb-4">
This modal displays a simple grid of images. Implement additional JavaScript if you need features like clicking thumbnails to show a larger image.
</p>
<!-- Image Grid -->
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-4">
<!-- Replace with your actual image sources and alt text -->
<div>
<img src="https://picsum.photos/200/150?random=11" alt="Gallery Image 1" class="w-full h-auto rounded shadow transition-transform duration-200 hover:scale-105 cursor-pointer">
</div>
<div>
<img src="https://picsum.photos/200/150?random=12" alt="Gallery Image 2" class="w-full h-auto rounded shadow transition-transform duration-200 hover:scale-105 cursor-pointer">
</div>
<div>
<img src="https://picsum.photos/200/150?random=13" alt="Gallery Image 3" class="w-full h-auto rounded shadow transition-transform duration-200 hover:scale-105 cursor-pointer">
</div>
<div>
<img src="https://picsum.photos/200/150?random=14" alt="Gallery Image 4" class="w-full h-auto rounded shadow transition-transform duration-200 hover:scale-105 cursor-pointer">
</div>
<div>
<img src="https://picsum.photos/200/150?random=15" alt="Gallery Image 5" class="w-full h-auto rounded shadow transition-transform duration-200 hover:scale-105 cursor-pointer">
</div>
<div>
<img src="https://picsum.photos/200/150?random=16" alt="Gallery Image 6" class="w-full h-auto rounded shadow transition-transform duration-200 hover:scale-105 cursor-pointer">
</div>
<div>
<img src="https://picsum.photos/200/150?random=17" alt="Gallery Image 7" class="w-full h-auto rounded shadow transition-transform duration-200 hover:scale-105 cursor-pointer">
</div>
<div>
<img src="https://picsum.photos/200/150?random=18" alt="Gallery Image 8" class="w-full h-auto rounded shadow transition-transform duration-200 hover:scale-105 cursor-pointer">
</div>
</div>
</div>
<!-- Modal Footer (Optional) -->
<div class="flex justify-end pt-3 border-t border-gray-200">
<button id="closeGalleryModalBtnFooter" class="px-4 bg-gray-200 p-3 rounded-lg text-gray-600 hover:bg-gray-300">Close</button>
</div>
</div>
</div>
<script>
const galleryModal = document.getElementById('galleryModal');
const openGalleryModalBtn = document.getElementById('openGalleryModalBtn');
const closeGalleryModalBtn = document.getElementById('closeGalleryModalBtn');
const closeGalleryModalBtnFooter = document.getElementById('closeGalleryModalBtnFooter');
const openGalleryModal = () => {
galleryModal.classList.remove('hidden');
document.body.style.overflow = 'hidden';
}
const closeGalleryModal = () => {
galleryModal.classList.add('hidden');
document.body.style.overflow = '';
}
openGalleryModalBtn.addEventListener('click', openGalleryModal);
closeGalleryModalBtn.addEventListener('click', closeGalleryModal);
if (closeGalleryModalBtnFooter) {
closeGalleryModalBtnFooter.addEventListener('click', closeGalleryModal);
}
galleryModal.addEventListener('click', (event) => {
// Check if the click is on the overlay itself (the flex container)
if (event.target === galleryModal) {
closeGalleryModal();
}
});
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && !galleryModal.classList.contains('hidden')) {
closeGalleryModal();
}
});
// Note: Add JS here to handle clicking images if needed (e.g., open a larger view)
</script>
Search Modal
Search Site
<!-- Trigger Button -->
<button id="openSearchModalBtn" class="p-2 rounded-md text-gray-500 hover:text-gray-700 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-blue-500">
<!-- Search Icon -->
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<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" />
</svg>
<span class="sr-only">Open Search</span>
</button>
<!-- Search Modal -->
<div id="searchModal" class="fixed inset-0 bg-gray-600 bg-opacity-75 backdrop-blur-sm overflow-y-auto h-full w-full z-50 hidden flex items-center justify-center"> <!-- Added flex centering -->
<div class="relative mx-auto p-5 border w-11/12 md:w-1/2 lg:w-1/3 shadow-lg rounded-md bg-white"> <!-- Removed top-1/4 -->
<!-- Close Button (Top Right) -->
<button id="closeSearchModalBtn" class="absolute top-0 right-0 mt-2 mr-3 text-gray-400 hover:text-gray-600 text-3xl font-light focus:outline-none z-10">×</button>
<!-- Search Form -->
<form action="/search" method="GET" class="mt-3"> <!-- Adjust action URL -->
<label for="modalSearchInput" class="block text-sm font-medium text-gray-700 mb-2">Search the site:</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" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z" clip-rule="evenodd" />
</svg>
</div>
<input
type="search"
name="query" <!-- Use a relevant name attribute -->
id="modalSearchInput"
class="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md leading-5 bg-white placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
placeholder="Enter search term..."
required
autofocus <!-- Automatically focus the input when modal opens -->
>
</div>
<div class="mt-4 text-right">
<button type="submit" class="inline-flex justify-center px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
Search
</button>
<button type="button" id="closeSearchModalBtnFooter" class="ml-2 inline-flex justify-center px-4 py-2 text-sm font-medium text-gray-700 bg-gray-100 border border-gray-300 rounded-md shadow-sm hover:bg-gray-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
Cancel
</button>
</div>
</form>
</div>
</div>
<script>
const searchModal = document.getElementById('searchModal');
const openSearchModalBtn = document.getElementById('openSearchModalBtn');
const closeSearchModalBtn = document.getElementById('closeSearchModalBtn');
const closeSearchModalBtnFooter = document.getElementById('closeSearchModalBtnFooter'); // Footer cancel button
const searchInput = document.getElementById('modalSearchInput'); // Search input field
const openSearchModal = () => {
searchModal.classList.remove('hidden');
document.body.style.overflow = 'hidden';
// Set focus to the search input shortly after the modal is displayed
setTimeout(() => searchInput.focus(), 50);
}
const closeSearchModal = () => {
searchModal.classList.add('hidden');
document.body.style.overflow = '';
// Optional: Clear search input on close
// searchInput.value = '';
}
openSearchModalBtn.addEventListener('click', openSearchModal);
closeSearchModalBtn.addEventListener('click', closeSearchModal);
if(closeSearchModalBtnFooter) {
closeSearchModalBtnFooter.addEventListener('click', closeSearchModal);
}
searchModal.addEventListener('click', (event) => {
// Check if the click is on the overlay itself (the flex container)
if (event.target === searchModal) {
closeSearchModal();
}
});
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && !searchModal.classList.contains('hidden')) {
closeSearchModal();
}
});
</script>
Filter Modal
Filter Options
<!-- Trigger Button -->
<button id="openFilterModalBtn" class="inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
<!-- Filter Icon -->
<svg class="-ml-1 mr-2 h-5 w-5 text-gray-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M3 3a1 1 0 011-1h12a1 1 0 011 1v3a1 1 0 01-.293.707L13 11.414V15a1 1 0 01-.293.707l-2 2A1 1 0 019 17v-5.586L4.293 6.707A1 1 0 014 6V3z" clip-rule="evenodd" />
</svg>
Filters
</button>
<!-- Filter Modal -->
<div id="filterModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50 hidden flex items-center justify-center"> <!-- Added flex centering -->
<div class="relative mx-auto p-5 border w-11/12 md:w-1/2 lg:w-1/3 shadow-lg rounded-md bg-white"> <!-- Removed top-10 -->
<!-- Modal Header -->
<div class="flex justify-between items-center pb-3 border-b">
<p class="text-xl font-semibold text-gray-800">Filter Options</p>
<button id="closeFilterModalBtn" class="text-gray-400 hover:text-gray-600 text-3xl font-light focus:outline-none">×</button>
</div>
<!-- Modal Body - Filter Form -->
<div class="my-5">
<form id="filterForm" action="/products" method="GET"> <!-- Adjust action/method as needed -->
<!-- Filter Section: Category -->
<div class="mb-6">
<label for="filterCategory" class="block text-sm font-medium text-gray-700 mb-2">Category</label>
<select id="filterCategory" name="category" class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded-md">
<option value="">All Categories</option>
<option value="electronics">Electronics</option>
<option value="clothing">Clothing</option>
<option value="books">Books</option>
<!-- Add more options -->
</select>
</div>
<!-- Filter Section: Price Range (Example using Radios) -->
<div class="mb-6">
<span class="block text-sm font-medium text-gray-700 mb-2">Price Range</span>
<div class="space-y-2">
<div class="flex items-center">
<input id="price_any" name="price_range" type="radio" value="" class="focus:ring-blue-500 h-4 w-4 text-blue-600 border-gray-300" checked>
<label for="price_any" class="ml-3 block text-sm text-gray-700">Any Price</label>
</div>
<div class="flex items-center">
<input id="price_low" name="price_range" type="radio" value="0-50" class="focus:ring-blue-500 h-4 w-4 text-blue-600 border-gray-300">
<label for="price_low" class="ml-3 block text-sm text-gray-700">$0 - $50</label>
</div>
<div class="flex items-center">
<input id="price_mid" name="price_range" type="radio" value="51-200" class="focus:ring-blue-500 h-4 w-4 text-blue-600 border-gray-300">
<label for="price_mid" class="ml-3 block text-sm text-gray-700">$51 - $200</label>
</div>
<div class="flex items-center">
<input id="price_high" name="price_range" type="radio" value="201+" class="focus:ring-blue-500 h-4 w-4 text-blue-600 border-gray-300">
<label for="price_high" class="ml-3 block text-sm text-gray-700">$201+</label>
</div>
</div>
</div>
<!-- Filter Section: Features (Example using Checkboxes) -->
<div class="mb-6">
<span class="block text-sm font-medium text-gray-700 mb-2">Features</span>
<div class="space-y-2">
<div class="relative flex items-start">
<div class="flex items-center h-5">
<input id="feature_instock" name="features[]" type="checkbox" value="in_stock" class="focus:ring-blue-500 h-4 w-4 text-blue-600 border-gray-300 rounded">
</div>
<div class="ml-3 text-sm">
<label for="feature_instock" class="font-medium text-gray-700">In Stock</label>
</div>
</div>
<div class="relative flex items-start">
<div class="flex items-center h-5">
<input id="feature_sale" name="features[]" type="checkbox" value="on_sale" class="focus:ring-blue-500 h-4 w-4 text-blue-600 border-gray-300 rounded">
</div>
<div class="ml-3 text-sm">
<label for="feature_sale" class="font-medium text-gray-700">On Sale</label>
</div>
</div>
</div>
</div>
<!-- Modal Footer - Actions -->
<div class="flex justify-end pt-4 border-t">
<button type="button" id="resetFilterBtn" class="px-4 py-2 bg-gray-200 text-gray-800 rounded-md hover:bg-gray-300 mr-2 focus:outline-none focus:ring-2 focus:ring-gray-400">
Reset
</button>
<button type="submit" id="applyFilterBtn" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2">
Apply Filters
</button>
</div>
</form>
</div>
</div>
</div>
<script>
const filterModal = document.getElementById('filterModal');
const openFilterModalBtn = document.getElementById('openFilterModalBtn');
const closeFilterModalBtn = document.getElementById('closeFilterModalBtn');
const filterForm = document.getElementById('filterForm'); // Form element
const resetFilterBtn = document.getElementById('resetFilterBtn'); // Reset button
const applyFilterBtn = document.getElementById('applyFilterBtn'); // Apply button
const openFilterModal = () => {
filterModal.classList.remove('hidden');
document.body.style.overflow = 'hidden';
}
const closeFilterModal = () => {
filterModal.classList.add('hidden');
document.body.style.overflow = '';
}
// Event listeners for modal open/close
openFilterModalBtn.addEventListener('click', openFilterModal);
closeFilterModalBtn.addEventListener('click', closeFilterModal);
// Optional: Reset form fields when Reset button is clicked
if (resetFilterBtn && filterForm) {
resetFilterBtn.addEventListener('click', () => {
filterForm.reset();
// You might need to manually reset custom components if any
});
}
// Optional: Close modal when Apply Filters is clicked (after form submission or AJAX call)
// If submitting traditionally, the page reload will close it.
// If using AJAX:
/*
if (applyFilterBtn && filterForm) {
applyFilterBtn.addEventListener('click', (event) => {
event.preventDefault(); // Prevent default form submission if using AJAX
// --- Add your AJAX logic here ---
// On success or completion:
closeFilterModal();
});
}
*/
// Close modal if background overlay is clicked
filterModal.addEventListener('click', (event) => {
// Check if the click is on the overlay itself (the flex container)
if (event.target === filterModal) {
closeFilterModal();
}
});
// Close modal with Escape key
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && !filterModal.classList.contains('hidden')) {
closeFilterModal();
}
});
// Make sure your form inputs have correct 'name' attributes for submission.
// If you're using this with a framework (React, Vue, Alpine),
// you'll likely handle state and submission differently.
</script>
<!-- For Tailwind Forms plugin styling -->
<!-- If using @tailwindcss/forms plugin, ensure it's installed and configured: -->
<!-- 1. npm install -D @tailwindcss/forms -->
<!-- 2. Add to tailwind.config.js: plugins: [require('@tailwindcss/forms')], -->
Settings Modal
Application Settings
<!-- Trigger Button (Example: Gear Icon) -->
<button id="openSettingsModalBtn" class="p-2 rounded-md text-gray-500 hover:text-gray-700 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-blue-500">
<svg class="h-6 w-6" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
<span class="sr-only">Open Settings</span>
</button>
<!-- Settings Modal -->
<div id="settingsModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50 hidden flex items-center justify-center"> <!-- Added flex centering -->
<div class="relative mx-auto p-5 border w-11/12 md:w-1/2 lg:w-1/3 shadow-lg rounded-md bg-white"> <!-- Removed top-1/4 -->
<!-- Modal Header -->
<div class="flex justify-between items-center pb-3 border-b">
<p class="text-xl font-semibold text-gray-800">Preferences</p>
<button id="closeSettingsModalBtn" class="text-gray-400 hover:text-gray-600 text-3xl font-light focus:outline-none">×</button>
</div>
<!-- Modal Body - Settings Form -->
<div class="my-5">
<form id="settingsForm"> <!-- Add action/method if submitting traditionally -->
<div class="space-y-6">
<!-- Setting 1: Toggle Switch -->
<div class="flex items-center justify-between">
<span class="flex-grow flex flex-col">
<span class="text-sm font-medium text-gray-900" id="setting1-label">Email Notifications</span>
<span class="text-sm text-gray-500" id="setting1-description">Receive updates via email.</span>
</span>
<!-- Tailwind UI Toggle Switch -->
<button
type="button"
id="toggleNotifications"
data-state="off" <!-- Use data-state for JS control -->
class="bg-gray-200 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
role="switch" aria-checked="false" aria-labelledby="setting1-label" aria-describedby="setting1-description">
<span class="sr-only">Enable email notifications</span>
<span
aria-hidden="true"
class="translate-x-0 pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200"></span>
</button>
<input type="hidden" name="notifications_enabled" id="notificationsEnabledInput" value="0"> <!-- Hidden input to store value -->
</div>
<!-- Setting 2: Another Toggle Switch -->
<div class="flex items-center justify-between">
<span class="flex-grow flex flex-col">
<span class="text-sm font-medium text-gray-900" id="setting2-label">Auto-Save Drafts</span>
</span>
<button
type="button"
id="toggleAutoSave"
data-state="on" <!-- Example: Default on -->
class="bg-blue-600 relative inline-flex flex-shrink-0 h-6 w-11 border-2 border-transparent rounded-full cursor-pointer transition-colors ease-in-out duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
role="switch" aria-checked="true" aria-labelledby="setting2-label">
<span class="sr-only">Enable auto-save</span>
<span
aria-hidden="true"
class="translate-x-5 pointer-events-none inline-block h-5 w-5 rounded-full bg-white shadow transform ring-0 transition ease-in-out duration-200"></span>
</button>
<input type="hidden" name="autosave_enabled" id="autosaveEnabledInput" value="1">
</div>
<!-- Setting 3: Select Dropdown -->
<div>
<label for="settingsTheme" class="block text-sm font-medium text-gray-700">Interface Theme</label>
<select id="settingsTheme" name="theme" class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded-md">
<option value="system">System Default</option>
<option value="light">Light Mode</option>
<option value="dark">Dark Mode</option>
</select>
</div>
</div>
<!-- Modal Footer - Actions -->
<div class="flex justify-end pt-6 border-t mt-6">
<button type="button" id="cancelSettingsBtn" class="px-4 py-2 bg-gray-100 text-gray-700 rounded-md hover:bg-gray-200 mr-2 focus:outline-none focus:ring-2 focus:ring-gray-400">
Cancel
</button>
<button type="submit" id="saveSettingsBtn" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2">
Save Changes
</button>
</div>
</form>
</div>
</div>
</div>
<script>
const settingsModal = document.getElementById('settingsModal');
const openSettingsModalBtn = document.getElementById('openSettingsModalBtn');
const closeSettingsModalBtn = document.getElementById('closeSettingsModalBtn');
const cancelSettingsBtn = document.getElementById('cancelSettingsBtn'); // Cancel button in footer
const saveSettingsBtn = document.getElementById('saveSettingsBtn'); // Save button
const settingsForm = document.getElementById('settingsForm'); // The form itself
// --- Toggle Switch Logic ---
const toggleButtons = settingsModal.querySelectorAll('button[role="switch"]');
toggleButtons.forEach(button => {
const hiddenInputId = button.id.replace('toggle', '') + 'EnabledInput'; // e.g., 'notificationsEnabledInput'
const hiddenInput = document.getElementById(hiddenInputId);
button.addEventListener('click', () => {
const currentState = button.getAttribute('data-state');
const newState = currentState === 'on' ? 'off' : 'on';
const isChecked = newState === 'on';
// Update button state and appearance
button.setAttribute('data-state', newState);
button.setAttribute('aria-checked', isChecked.toString());
button.classList.toggle('bg-blue-600', isChecked);
button.classList.toggle('bg-gray-200', !isChecked);
button.querySelector('span[aria-hidden="true"]').classList.toggle('translate-x-5', isChecked);
button.querySelector('span[aria-hidden="true"]').classList.toggle('translate-x-0', !isChecked);
// Update hidden input value
if (hiddenInput) {
hiddenInput.value = isChecked ? '1' : '0';
console.log(`${hiddenInput.name} set to: ${hiddenInput.value}`); // For debugging
}
});
});
// --- End Toggle Switch Logic ---
const openSettingsModal = () => {
settingsModal.classList.remove('hidden');
document.body.style.overflow = 'hidden';
// Add logic here to load current settings into the form fields if needed
}
const closeSettingsModal = () => {
settingsModal.classList.add('hidden');
document.body.style.overflow = '';
// Optional: Reset form to original state or last saved state on cancel?
}
// Event listeners for modal open/close
openSettingsModalBtn.addEventListener('click', openSettingsModal);
closeSettingsModalBtn.addEventListener('click', closeSettingsModal);
if (cancelSettingsBtn) {
cancelSettingsBtn.addEventListener('click', closeSettingsModal);
}
// Handle form submission (example: just close modal)
if (settingsForm && saveSettingsBtn) {
settingsForm.addEventListener('submit', (event) => {
event.preventDefault(); // Prevent default if using AJAX or just demonstrating
console.log('Settings form submitted (or Save clicked)');
// --- Add your save logic here (e.g., AJAX request) ---
// Get form data: const formData = new FormData(settingsForm);
// for (let [key, value] of formData.entries()) { console.log(key, value); }
// On success:
closeSettingsModal();
});
}
// Close modal if background overlay is clicked
settingsModal.addEventListener('click', (event) => {
// Check if the click is on the overlay itself (the flex container)
if (event.target === settingsModal) {
closeSettingsModal();
}
});
// Close modal with Escape key
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && !settingsModal.classList.contains('hidden')) {
closeSettingsModal();
}
});
</script>
<!-- Requires Tailwind UI plugin or custom toggle styles if not using default HTML checkbox -->
Profile Edit Modal
Edit Your Profile
<!-- Trigger Button -->
<button id="openProfileModalBtn" class="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded">
Edit Profile
</button>
<!-- Profile Edit Modal -->
<div id="profileModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50 hidden flex items-center justify-center"> <!-- Added flex centering -->
<div class="relative mx-auto p-5 border w-11/12 md:w-1/2 lg:w-1/3 shadow-lg rounded-md bg-white"> <!-- Removed top-10 -->
<!-- Modal Header -->
<div class="flex justify-between items-center pb-3 border-b">
<p class="text-xl font-semibold text-gray-800">Update Your Profile</p>
<button id="closeProfileModalBtn" class="text-gray-400 hover:text-gray-600 text-3xl font-light focus:outline-none">×</button>
</div>
<!-- Modal Body - Profile Form -->
<div class="my-5">
<form id="profileForm" method="POST" action="/profile/update" enctype="multipart/form-data"> <!-- Adjust action/method/enctype -->
<input type="hidden" name="_token" value="YOUR_CSRF_TOKEN"> <!-- Example for CSRF token -->
<div class="space-y-6">
<!-- Profile Picture Section -->
<div>
<label class="block text-sm font-medium text-gray-700"> Profile Photo </label>
<div class="mt-1 flex items-center space-x-5">
<span class="inline-block h-16 w-16 rounded-full overflow-hidden bg-gray-100">
<!-- Current Image Placeholder or Actual Image -->
<img id="profileImagePreview" class="h-full w-full text-gray-300" src="https://picsum.photos/100/100?random=25" alt="Current Profile Photo">
<!-- Or use an SVG placeholder -->
<!--
<svg class="h-full w-full text-gray-300" fill="currentColor" viewBox="0 0 24 24">
<path d="M24 20.993V24H0v-2.996A14.977 14.977 0 0112.004 15c4.904 0 9.26 2.354 11.996 5.993zM16.002 8.999a4 4 0 11-8 0 4 4 0 018 0z" />
</svg>
-->
</span>
<div>
<label for="profile_photo" class="cursor-pointer bg-white py-2 px-3 border border-gray-300 rounded-md shadow-sm text-sm leading-4 font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
Change
</label>
<input id="profile_photo" name="profile_photo" type="file" class="sr-only" accept="image/*">
<p class="text-xs text-gray-500 mt-1">PNG, JPG, GIF up to 2MB</p>
</div>
</div>
</div>
<!-- Name Field -->
<div>
<label for="profile_name" class="block text-sm font-medium text-gray-700">Full Name</label>
<input type="text" name="name" id="profile_name" autocomplete="name" required
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
value="Current User Name"> <!-- Pre-fill with current data -->
</div>
<!-- Email Field -->
<div>
<label for="profile_email" class="block text-sm font-medium text-gray-700">Email Address</label>
<input type="email" name="email" id="profile_email" autocomplete="email" required
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
value="current.user@example.com"> <!-- Pre-fill with current data -->
</div>
<!-- Bio Field -->
<div>
<label for="profile_bio" class="block text-sm font-medium text-gray-700">About You</label>
<textarea id="profile_bio" name="bio" rows="3"
class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
placeholder="Tell us a little about yourself">Current Bio Text</textarea> <!-- Pre-fill -->
</div>
</div>
<!-- Modal Footer - Actions -->
<div class="flex justify-end pt-6 border-t mt-6">
<button type="button" id="cancelProfileBtn" class="px-4 py-2 bg-gray-100 text-gray-700 rounded-md hover:bg-gray-200 mr-2 focus:outline-none focus:ring-2 focus:ring-gray-400">
Cancel
</button>
<button type="submit" id="saveProfileBtn" class="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2">
Save Changes
</button>
</div>
</form>
</div>
</div>
</div>
<script>
const profileModal = document.getElementById('profileModal');
const openProfileModalBtn = document.getElementById('openProfileModalBtn');
const closeProfileModalBtn = document.getElementById('closeProfileModalBtn');
const cancelProfileBtn = document.getElementById('cancelProfileBtn'); // Footer cancel button
const saveProfileBtn = document.getElementById('saveProfileBtn'); // Save button
const profileForm = document.getElementById('profileForm'); // The form
const profilePhotoInput = document.getElementById('profile_photo'); // File input
const profileImagePreview = document.getElementById('profileImagePreview'); // Image preview element
const openProfileModal = () => {
profileModal.classList.remove('hidden');
document.body.style.overflow = 'hidden';
// Add logic here to fetch and pre-fill form fields with current user data
}
const closeProfileModal = () => {
profileModal.classList.add('hidden');
document.body.style.overflow = '';
// Optional: Reset form fields or image preview on cancel?
// profileForm.reset();
// profileImagePreview.src = 'default-placeholder.png';
}
// Event listeners for modal open/close
openProfileModalBtn.addEventListener('click', openProfileModal);
closeProfileModalBtn.addEventListener('click', closeProfileModal);
if (cancelProfileBtn) {
cancelProfileBtn.addEventListener('click', closeProfileModal);
}
// Handle form submission (example: prevent default and log)
if (profileForm) {
profileForm.addEventListener('submit', (event) => {
event.preventDefault(); // Remove if submitting traditionally
console.log('Profile form submitted');
// --- Add your update logic here (e.g., AJAX with FormData) ---
// const formData = new FormData(profileForm);
// fetch('/profile/update', { method: 'POST', body: formData }) ...
// On success:
closeProfileModal();
});
}
// Optional: Image preview for file input
if (profilePhotoInput && profileImagePreview) {
profilePhotoInput.addEventListener('change', function(event) {
const file = event.target.files[0];
if (file && file.type.startsWith('image/')) {
const reader = new FileReader();
reader.onload = function(e) {
profileImagePreview.src = e.target.result;
}
reader.readAsDataURL(file);
} else {
// Optionally reset preview if file is not an image or is cleared
// profileImagePreview.src = 'default-placeholder.png';
}
});
}
// Close modal if background overlay is clicked
profileModal.addEventListener('click', (event) => {
// Check if the click is on the overlay itself (the flex container)
if (event.target === profileModal) {
closeProfileModal();
}
});
// Close modal with Escape key
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && !profileModal.classList.contains('hidden')) {
closeProfileModal();
}
});
</script>
<!-- Ensure you handle CSRF tokens and validation server-side -->
<!-- Uses @tailwindcss/forms plugin styling for inputs/textarea/select -->
Tooltip-Like Modal (Info Popup)
Quick Help
This is a small modal providing brief information, similar to an expanded tooltip.
<!-- Trigger Element (e.g., an icon button) -->
<button id="openTooltipModalBtn" class="inline-flex items-center justify-center w-6 h-6 rounded-full bg-gray-200 text-gray-600 hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
<span class="sr-only">Show information</span>
<!-- Info Icon -->
<svg class="w-4 h-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
</button>
<!-- Tooltip-Like Modal -->
<div id="tooltipModal" class="fixed inset-0 bg-gray-600 bg-opacity-25 overflow-y-auto h-full w-full z-50 hidden flex items-center justify-center p-4"> <!-- Added flex centering, slight overlay, padding -->
<!-- Modal Content -->
<!-- Removed absolute positioning, using flex centering instead -->
<div id="tooltipModalContent" class="relative p-4 border w-auto max-w-xs shadow-lg rounded-md bg-white">
<!-- Optional Close Button -->
<button id="closeTooltipModalBtn" class="absolute top-0 right-0 mt-1 mr-1 text-gray-300 hover:text-gray-500 text-xl font-light focus:outline-none z-10">×</button>
<!-- Modal Header (Optional) -->
<h4 class="text-md font-medium text-gray-800 mb-2">Information</h4>
<!-- Modal Body -->
<p class="text-sm text-gray-600">
This provides a brief explanation or help text related to the element that triggered it. Keep it concise.
</p>
<!-- Optional Footer/Action -->
<div class="text-right mt-3">
<button id="dismissTooltipModalBtn" class="text-xs bg-blue-100 hover:bg-blue-200 text-blue-700 px-2 py-1 rounded">Got it</button>
</div>
</div>
</div>
<script>
const tooltipModal = document.getElementById('tooltipModal');
const openTooltipModalBtn = document.getElementById('openTooltipModalBtn');
const closeTooltipModalBtn = document.getElementById('closeTooltipModalBtn');
const dismissTooltipModalBtn = document.getElementById('dismissTooltipModalBtn');
const tooltipModalContent = document.getElementById('tooltipModalContent'); // Content div
const openTooltipModal = () => {
tooltipModal.classList.remove('hidden');
// No complex positioning needed now, flex handles centering.
}
const closeTooltipModal = () => {
tooltipModal.classList.add('hidden');
}
// Event listeners
openTooltipModalBtn.addEventListener('click', openTooltipModal);
closeTooltipModalBtn.addEventListener('click', closeTooltipModal);
if (dismissTooltipModalBtn) {
dismissTooltipModalBtn.addEventListener('click', closeTooltipModal);
}
// Close modal if clicked on the overlay (outside the content area)
tooltipModal.addEventListener('click', (event) => {
// Check if the click target is the overlay itself
if (event.target === tooltipModal) {
closeTooltipModal();
}
});
// Close modal with Escape key
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && !tooltipModal.classList.contains('hidden')) {
closeTooltipModal();
}
});
</script>
Multi-Step/Wizard Modal
Setup Process (Step 1 of 3)
Step 1: Basic Information
Step 2: Configuration
Step 3: Confirmation
Review your settings before finishing.
<!-- Trigger Button -->
<button id="openWizardModalBtn" class="bg-teal-500 hover:bg-teal-700 text-white font-bold py-2 px-4 rounded">
Start Setup
</button>
<!-- Multi-Step/Wizard Modal -->
<div id="wizardModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50 hidden flex items-center justify-center p-4"> <!-- Added flex centering & padding -->
<div class="relative mx-auto p-5 border w-11/12 md:w-2/3 lg:w-1/2 shadow-lg rounded-md bg-white"> <!-- Removed top-10 -->
<!-- Modal Header -->
<div class="flex justify-between items-center pb-3 border-b">
<p class="text-xl font-semibold text-gray-800">Configuration Wizard - Step <span id="wizardStepIndicator">1</span></p>
<button id="closeWizardModalBtn" class="text-gray-400 hover:text-gray-600 text-3xl font-light focus:outline-none">×</button>
</div>
<!-- Modal Body - Steps Container -->
<div class="my-5">
<form id="wizardForm"> <!-- Wrap steps in a form if collecting data -->
<!-- Step 1 -->
<div id="wizardStep-1" class="wizard-step active-step space-y-4">
<h4 class="text-lg font-medium text-gray-900">Account Details</h4>
<p class="text-sm text-gray-600">Please provide your basic account information.</p>
<div>
<label for="wizard_email" class="block text-sm font-medium text-gray-700">Email Address</label>
<input type="email" name="email" id="wizard_email" required class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm">
</div>
<div>
<label for="wizard_password" class="block text-sm font-medium text-gray-700">Password</label>
<input type="password" name="password" id="wizard_password" required class="mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm">
</div>
</div>
<!-- Step 2 -->
<div id="wizardStep-2" class="wizard-step hidden space-y-4">
<h4 class="text-lg font-medium text-gray-900">Preferences</h4>
<p class="text-sm text-gray-600">Choose your preferred settings.</p>
<div>
<label for="wizard_theme" class="block text-sm font-medium text-gray-700">Theme</label>
<select name="theme" id="wizard_theme" class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded-md">
<option>Light</option>
<option>Dark</option>
<option>System</option>
</select>
</div>
<div class="relative flex items-start">
<div class="flex items-center h-5">
<input id="wizard_notifications" name="notifications" type="checkbox" value="true" class="focus:ring-blue-500 h-4 w-4 text-blue-600 border-gray-300 rounded">
</div>
<div class="ml-3 text-sm">
<label for="wizard_notifications" class="font-medium text-gray-700">Enable Notifications</label>
</div>
</div>
</div>
<!-- Step 3 -->
<div id="wizardStep-3" class="wizard-step hidden space-y-4">
<h4 class="text-lg font-medium text-gray-900">Confirmation</h4>
<p class="text-sm text-gray-600">You're all set! Please review your information and click Finish.</p>
<!-- Optionally display a summary of choices here -->
<div class="bg-gray-50 p-4 rounded-md border">
<p class="text-sm"><strong>Email:</strong> <span id="summary_email">[Email from Step 1]</span></p>
<p class="text-sm"><strong>Theme:</strong> <span id="summary_theme">[Theme from Step 2]</span></p>
</div>
</div>
</form>
</div>
<!-- Modal Footer - Navigation -->
<div class="flex justify-between items-center pt-4 border-t mt-6">
<button id="wizardPrevBtn" type="button" class="px-4 py-2 bg-gray-200 text-gray-800 rounded-md hover:bg-gray-300 disabled:opacity-50 disabled:cursor-not-allowed focus:outline-none focus:ring-2 focus:ring-gray-400" disabled>
Previous
</button>
<div>
<button id="wizardNextBtn" type="button" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2">
Next
</button>
<button id="wizardFinishBtn" type="submit" form="wizardForm" class="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 hidden"> <!-- Initially hidden -->
Finish
</button>
</div>
</div>
</div>
</div>
<style>
/* Basic style to hide/show steps - included here for simplicity */
.wizard-step { display: none; }
.wizard-step.active-step { display: block; }
</style>
<script>
const wizardModal = document.getElementById('wizardModal');
const openWizardModalBtn = document.getElementById('openWizardModalBtn');
const closeWizardModalBtn = document.getElementById('closeWizardModalBtn');
const wizardPrevBtn = document.getElementById('wizardPrevBtn');
const wizardNextBtn = document.getElementById('wizardNextBtn');
const wizardFinishBtn = document.getElementById('wizardFinishBtn');
const wizardSteps = wizardModal.querySelectorAll('.wizard-step');
const stepIndicator = document.getElementById('wizardStepIndicator');
const wizardForm = document.getElementById('wizardForm'); // The form
let currentStep = 0; // 0-indexed
const totalSteps = wizardSteps.length;
const showStep = (stepIndex) => {
wizardSteps.forEach((step, index) => {
step.classList.toggle('active-step', index === stepIndex);
step.classList.toggle('hidden', index !== stepIndex); // Ensure non-active are hidden
});
if (stepIndicator) stepIndicator.textContent = stepIndex + 1;
// Update button states
wizardPrevBtn.disabled = stepIndex === 0;
wizardNextBtn.classList.toggle('hidden', stepIndex === totalSteps - 1);
wizardFinishBtn.classList.toggle('hidden', stepIndex !== totalSteps - 1);
// Optional: Update summary on last step
if (stepIndex === totalSteps - 1) {
updateConfirmationSummary();
}
};
const openWizardModal = () => {
currentStep = 0; // Reset to first step on open
showStep(currentStep);
wizardModal.classList.remove('hidden');
document.body.style.overflow = 'hidden';
// Optional: Reset form fields
// if (wizardForm) wizardForm.reset();
}
const closeWizardModal = () => {
wizardModal.classList.add('hidden');
document.body.style.overflow = '';
}
// Basic validation example for 'Next' click
const validateStep = (stepIndex) => {
const currentStepElement = wizardSteps[stepIndex];
const inputs = currentStepElement.querySelectorAll('input[required], select[required]');
let isValid = true;
inputs.forEach(input => {
// Remove previous error states
input.classList.remove('border-red-500');
if (!input.value.trim()) { // Use trim() to check for empty spaces
// Basic feedback: add a red border
input.classList.add('border-red-500');
isValid = false;
}
});
if (!isValid) {
// Find the first invalid input and focus it (optional)
const firstInvalid = currentStepElement.querySelector('.border-red-500');
if(firstInvalid) firstInvalid.focus();
// alert('Please fill in all required fields for this step.'); // Consider less intrusive feedback
}
return isValid;
};
// Optional: Function to update summary on the last step
const updateConfirmationSummary = () => {
const emailInput = document.getElementById('wizard_email');
const themeSelect = document.getElementById('wizard_theme');
const emailSummary = document.getElementById('summary_email');
const themeSummary = document.getElementById('summary_theme');
if (emailInput && emailSummary) emailSummary.textContent = emailInput.value || '[Not provided]';
if (themeSelect && themeSummary) themeSummary.textContent = themeSelect.options[themeSelect.selectedIndex].text || '[Not selected]';
};
// Button Event Listeners
openWizardModalBtn.addEventListener('click', openWizardModal);
closeWizardModalBtn.addEventListener('click', closeWizardModal);
wizardPrevBtn.addEventListener('click', () => {
if (currentStep > 0) {
currentStep--;
showStep(currentStep);
}
});
wizardNextBtn.addEventListener('click', () => {
// Add validation before proceeding
if (!validateStep(currentStep)) {
return; // Stop if validation fails
}
if (currentStep < totalSteps - 1) {
currentStep++;
showStep(currentStep);
}
});
// Handle Finish button (likely form submission)
if (wizardForm && wizardFinishBtn) {
wizardForm.addEventListener('submit', (event) => {
event.preventDefault(); // Prevent default if using AJAX or just logging
console.log('Wizard form submitted!');
// --- Add your final submission logic here ---
// const formData = new FormData(wizardForm);
// fetch('/submit-wizard', { method: 'POST', body: formData })...
// On success:
alert('Setup Complete!');
closeWizardModal();
});
}
// Close modal with Escape key or overlay click
wizardModal.addEventListener('click', (event) => {
// Check if the click is on the overlay itself (the flex container)
if (event.target === wizardModal) closeWizardModal();
});
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && !wizardModal.classList.contains('hidden')) closeWizardModal();
});
// Initial setup
showStep(currentStep); // Show the first step initially
</script>
<!-- Requires @tailwindcss/forms for consistent input styling -->
Scrollable Content Modal
Terms and Conditions
Agreement Details
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla.
User Obligations
Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur sodales ligula in libero. Sed dignissim lacinia nunc. Curabitur tortor. Pellentesque nibh. Aenean quam. In scelerisque sem at dolor. Maecenas mattis. Sed convallis tristique sem. Proin ut ligula vel nunc egestas porttitor.
Morbi lectus risus, iaculis vel, suscipit quis, luctus non, massa. Fusce ac turpis quis ligula lacinia aliquet. Mauris ipsum. Nulla metus metus, ullamcorper vel, tincidunt sed, euismod in, nibh. Quisque volutpat condimentum velit.
Disclaimers
Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nam nec ante. Sed lacinia, urna non tincidunt mattis, tortor neque adipiscing diam, a cursus ipsum ante quis turpis. Nulla facilisi. Ut fringilla. Suspendisse potenti. Nunc feugiat mi a tellus consequat imperdiet. Vestibulum sapien. Proin quam. Etiam ultrices.
Suspendisse in justo eu magna luctus suscipit. Sed lectus. Integer euismod lacus luctus magna. Quisque cursus, metus vitae pharetra auctor, sem massa mattis sem, at interdum magna augue eget diam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Morbi lacinia molestie dui. Praesent blandit dolor. Sed non quam. In vel mi sit amet augue congue elementum.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent mauris. Fusce nec tellus sed augue semper porta. Mauris massa. Vestibulum lacinia arcu eget nulla.
Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Curabitur sodales ligula in libero. Sed dignissim lacinia nunc. Curabitur tortor. Pellentesque nibh. Aenean quam. In scelerisque sem at dolor. Maecenas mattis. Sed convallis tristique sem. Proin ut ligula vel nunc egestas porttitor.
<!-- Trigger Button -->
<button id="openScrollableModalBtn" class="text-sm text-blue-600 hover:underline">
View Terms of Service
</button>
<!-- Scrollable Content Modal -->
<div id="scrollableModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50 hidden flex items-center justify-center p-4"> <!-- Added flex centering & padding -->
<!-- Modal Dialog: Use flex column and max-height -->
<div class="relative mx-auto p-0 border w-11/12 md:w-2/3 lg:w-1/2 shadow-lg rounded-md bg-white flex flex-col max-h-[85vh]"> <!-- Adjust max-h-xx as needed, Removed top-10, padding handled internally -->
<!-- Modal Header (Sticky or Fixed within Modal) -->
<div class="flex justify-between items-center p-5 border-b flex-shrink-0 bg-white rounded-t-md"> <!-- Added padding & bg -->
<p class="text-xl font-semibold text-gray-800">Agreement</p>
<button id="closeScrollableModalBtn" class="text-gray-400 hover:text-gray-600 text-3xl font-light focus:outline-none">×</button>
</div>
<!-- Modal Body (Scrollable) -->
<div class="p-5 overflow-y-auto flex-grow"> <!-- Added padding & overflow -->
<h3 class="text-lg font-medium mb-2">Section 1: Introduction</h3>
<p class="text-sm text-gray-600 mb-4">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
<h3 class="text-lg font-medium mb-2">Section 2: User Responsibilities</h3>
<ul class="list-disc list-inside text-sm text-gray-600 mb-4 space-y-1">
<li>Maintain the confidentiality of your account information.</li>
<li>Use the service in compliance with all applicable laws.</li>
<li>Do not engage in any activity that disrupts or interferes with the service.</li>
<li>Report any unauthorized use of your account immediately.</li>
</ul>
<h3 class="text-lg font-medium mb-2">Section 3: Content Ownership</h3>
<p class="text-sm text-gray-600 mb-4">
Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec velit neque, auctor sit amet aliquam vel, ullamcorper sit amet ligula. Curabitur aliquet quam id dui posuere blandit. Vivamus suscipit tortor eget felis porttitor volutpat. Nulla quis lorem ut libero malesuada feugiat.
</p>
<h3 class="text-lg font-medium mb-2">Section 4: Disclaimers and Limitation of Liability</h3>
<p class="text-sm text-gray-600 mb-4">
THE SERVICE IS PROVIDED "AS IS" AND "AS AVAILABLE" WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. WE DISCLAIM ALL WARRANTIES, INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COMPANY BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL OR PUNITIVE DAMAGES.
</p>
<!-- Add much more content here to demonstrate scrolling -->
<p class="text-sm text-gray-600 mb-4">
Pellentesque in ipsum id orci porta dapibus. Proin eget tortor risus. Curabitur arcu erat, accumsan id imperdiet et, porttitor at sem. Vivamus magna justo, lacinia eget consectetur sed, convallis at tellus. Cras ultricies ligula sed magna dictum porta.
</p>
<p class="text-sm text-gray-600 mb-4">
Donec sollicitudin molestie malesuada. Curabitur non nulla sit amet nisl tempus convallis quis ac lectus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla porttitor accumsan tincidunt. Mauris blandit aliquet elit, eget tincidunt nibh pulvinar a.
</p>
<p class="text-sm text-gray-600 mb-4">
Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.
</p>
<p class="text-sm text-gray-600 mb-4">
Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur?
</p>
</div>
<!-- Modal Footer (Sticky or Fixed within Modal) -->
<div class="flex justify-end items-center p-5 border-t flex-shrink-0 bg-gray-50 rounded-b-md"> <!-- Added padding & bg -->
<button id="declineScrollableBtn" type="button" class="px-4 py-2 bg-gray-200 text-gray-800 rounded-md hover:bg-gray-300 mr-2 focus:outline-none focus:ring-2 focus:ring-gray-400">
Decline
</button>
<button id="acceptScrollableBtn" type="button" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2">
Accept
</button>
</div>
</div>
</div>
<script>
const scrollableModal = document.getElementById('scrollableModal');
const openScrollableModalBtn = document.getElementById('openScrollableModalBtn');
const closeScrollableModalBtn = document.getElementById('closeScrollableModalBtn'); // Header close
const declineScrollableBtn = document.getElementById('declineScrollableBtn'); // Footer decline
const acceptScrollableBtn = document.getElementById('acceptScrollableBtn'); // Footer accept
const openScrollableModal = () => {
scrollableModal.classList.remove('hidden');
document.body.style.overflow = 'hidden'; // Prevent background scroll
}
const closeScrollableModal = () => {
scrollableModal.classList.add('hidden');
document.body.style.overflow = ''; // Restore background scroll
}
// Event listeners
openScrollableModalBtn.addEventListener('click', openScrollableModal);
closeScrollableModalBtn.addEventListener('click', closeScrollableModal);
if (declineScrollableBtn) {
declineScrollableBtn.addEventListener('click', closeScrollableModal);
}
if (acceptScrollableBtn) {
acceptScrollableBtn.addEventListener('click', () => {
console.log('Terms Accepted'); // Add acceptance logic here
closeScrollableModal();
});
}
// Close modal if background overlay is clicked
scrollableModal.addEventListener('click', (event) => {
// Check if the click is on the overlay itself (the flex container)
if (event.target === scrollableModal) {
closeScrollableModal();
}
});
// Close modal with Escape key
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && !scrollableModal.classList.contains('hidden')) {
closeScrollableModal();
}
});
</script>
<!-- Key classes: max-h-[xxvh], flex, flex-col on modal dialog; flex-grow, overflow-y-auto on body; flex-shrink-0 on header/footer -->
FAQ on Tailwind CSS Modals
How do I create a basic modal with Tailwind CSS?
Create a fixed-position div with appropriate z-index and add overlay styles. Inside, place your modal content div with background color and padding. Toggle visibility using JavaScript or Alpine.js by manipulating CSS classes. Tailwind UI offers pre-built components if you need shortcuts.
Can I animate modal transitions in Tailwind?
Yes. Use Tailwind's transition utilities alongside opacity and transform classes to create smooth animations. For more advanced modal animations, combine with CSS keyframes or leverage headless UI library's transition components. Alpine.js pairs well for controlling animation states.
How do I make Tailwind modals accessible?
Focus on proper ARIA attributes (role="dialog"), keyboard interactions, and focus trapping. Ensure screen readers can navigate modal content. Headless UI provides accessible dialog components out of the box that follow WAI-ARIA practices. Remember to manage focus when modal closes.
Do I need JavaScript for Tailwind modals?
Yes. While Tailwind CSS handles styling through utility classes, you'll need JavaScript for modal functionality like opening, closing, and backdrop clicks. Options include vanilla JS, Alpine.js for simplicity, or framework-specific solutions in React or Vue environments.
How do I center a modal vertically and horizontally?
Use Tailwind's flex utilities: fixed inset-0 flex items-center justify-center. This creates a container spanning the viewport with centered content. Add z-50 for proper stacking and style your backdrop separately with lower z-index for overlay screens.
Can Tailwind modals be responsive?
Absolutely. Use Tailwind's responsive prefixes (sm:, md:, lg:) to adjust modal width, padding, and positioning across breakpoints. Consider different layouts for mobile devices versus desktops. Test thoroughly across different screen sizes for optimal user experience.
How do I close a modal when clicking outside?
Implement event listeners that check if the click target is the backdrop element. With Alpine.js: @click.self="open = false" on the overlay. In React with headless UI, use the onClose prop. Always provide explicit close buttons too for better accessibility.
Should I use Tailwind plugins for modals?
Consider Headless UI (from Tailwind Labs) for production-grade modal components with built-in accessibility. For simpler projects, custom implementation works fine. Evaluate TailwindUI if you need professionally designed modal templates that save development time.
How do I prevent body scrolling when modal is open?
Add overflow-hidden to the body element when opening your modal. Remember to remove it when closing. For iOS Safari compatibility, you might need additional fixes. Libraries like body-scroll-lock can help manage this behavior across web browsers.
Can I nest modals with Tailwind CSS?
Yes, but carefully manage z-index values and focus management. Nested dialog components require thoughtful state management and proper DOM structure. Consider whether a different UI pattern might better serve your users before implementing stacked modal windows.