Modals & Dialogs
MediumOverlay dialogs with backdrop blur, smooth animations, and keyboard accessibility
Live Demo
Basic Modal
Simple modal dialog with backdrop blur and smooth animations
Modal Title
This is the modal content area. Add any content here.
Resize to see how the component adapts to different screen sizes
📖 Basic Modal Overview
This modal component provides a clean, accessible dialog overlay with backdrop blur, smooth animations, and comprehensive keyboard navigation. Perfect for confirmations, forms, and displaying additional content without leaving the current page.
🎯 Key Features
- • Backdrop Blur - Semi-transparent overlay with visual separation
- • Keyboard Accessible - Escape key closes modal, proper focus management
- • Click Outside - Close modal by clicking backdrop area
- • Body Scroll Lock - Prevents background scrolling when modal is open
- • Focus Management - Automatically focuses first interactive element
- • Smooth Animations - Consistent transitions for opening and closing
💡 Best Practices
- • Use for confirmations, forms, or detailed content views
- • Always provide clear close actions (X button, Cancel, etc.)
- • Keep modal content focused and concise
- • Include proper ARIA labels for accessibility
- • Test keyboard navigation and screen reader compatibility
🏗️ Basic Modal HTML Structure
The modal uses a layered approach with proper semantic HTML and comprehensive Stimulus integration for accessibility and interaction.
📋 Stimulus Integration
data-controller="interactive--modals--basic"
Connects the modal to the Stimulus controller for all interactions
data-action="click->interactive--modals--basic#open"
Opens the modal when trigger button is clicked
data-interactive--modals--basic-target="container"
Identifies the full-screen modal overlay container
data-interactive--modals--basic-target="panel"
Identifies the modal content panel for focus management
data-action="click->interactive--modals--basic#closeOnBackdrop"
Closes modal when clicking outside the content area
🏗️ Modal Structure
- • Trigger Button - Semantic button element with clear labeling
- • Container - Fixed overlay covering entire viewport (z-index: 50)
- • Backdrop - Semi-transparent background with click-to-close
- • Panel - Modal content with proper positioning and styling
- • Header - Title area with close button and proper typography
- • Content - Main modal body for any content
- • Footer - Action buttons with consistent spacing
♿ Accessibility Features
- •
aria-label="Close"
on close button for screen readers - • Focus automatically moves to first interactive element
- • Escape key closes modal (keyboard navigation)
- • Body scroll is locked when modal is open
- • High contrast close icon with proper sizing
🎨 Basic Modal Styling
The modal uses carefully crafted Tailwind classes to create a professional, accessible dialog with smooth animations and forest theme integration.
🎨 Forest Theme Colors
Modal Panel
bg-forest-50
border-forest-200
Backdrop Overlay
bg-black/50
Footer Area
bg-forest-100
📐 Layout & Positioning
fixed inset-0
- Full viewport overlayz-50
- High z-index for overlayflex items-center justify-center
- Center modalmax-w-md mx-auto
- Responsive widthrounded-xl
- Rounded modal cornersshadow-xl
- Prominent drop shadowoverflow-hidden
- Clean edges🎯 Button Styling
Primary Action
bg-forest-700
hover:bg-forest-800
Secondary Action
bg-forest-200
hover:bg-forest-300
⚡ Animations & Transitions
- •
transition-colors duration-200
- Smooth color changes - •
hidden
class toggles visibility - • Backdrop blur creates depth perception
- • Focus states provide clear interaction feedback
📱 Responsive Design
- •
w-full max-w-md
- Scales from mobile to desktop - • Adequate padding on small screens
- • Touch-friendly button sizes (44px minimum)
- • Proper spacing in footer button groups
- • Modal centers properly on all screen sizes
⚡ Basic Modal JavaScript Logic
The Basic Modal controller provides comprehensive modal functionality with accessibility features, keyboard navigation, focus management, and smooth interactions for an optimal user experience.
🎯 Controller Features
Targets
container
- Full overlay element
panel
- Modal content area
Actions
open
- Shows modal with focus
close
- Hides modal
closeOnBackdrop
- Click outside to close
Values
No configuration values - works out of the box
Lifecycle
connect
- Binds keyboard events
disconnect
- Cleanup on removal
🔄 Modal Opening Flow
- 1. User clicks trigger button
- 2.
open()
method removeshidden
class from container - 3. Body scroll is locked with
overflow-hidden
- 4. Escape key listener is added to document
- 5. Focus automatically moves to first interactive element
- 6. Modal is now fully accessible and interactive
🔄 Modal Closing Flow
- 1. User triggers close (button, backdrop, or Escape key)
- 2.
close()
method addshidden
class to container - 3. Body scroll is restored by removing
overflow-hidden
- 4. Escape key listener is removed from document
- 5. Focus returns to the trigger element (natural browser behavior)
🔥 Advanced Features
- • Keyboard Accessibility - Escape key closes modal from anywhere
- • Focus Management - Auto-focus first interactive element on open
- • Backdrop Click - Click outside modal content to close
- • Body Scroll Lock - Prevents background scrolling when open
- • Event Cleanup - Removes listeners on disconnect to prevent leaks
- • Accessibility First - Proper ARIA labels and semantic structure
💡 Implementation Details
- •
boundKeydown
binding prevents memory leaks - •
setTimeout
ensures DOM is ready before focusing - •
event.target === event.currentTarget
ensures backdrop-only clicks - • Proper cleanup in
disconnect()
method for single-page apps
<div data-controller="modals-basic">
<button
class="px-6 py-3 bg-green-600 hover:bg-green-700 text-green-50 font-medium rounded-lg transition-colors duration-200"
data-action="click->modals-basic#open"
>
Open Modal Demo
</button>
<div data-modals-basic-target="container" class="hidden fixed inset-0 z-50 flex items-center justify-center">
<div data-action="click->modals-basic#closeOnBackdrop" class="absolute inset-0 bg-black/50"></div>
<div data-modals-basic-target="panel" class="relative z-10 w-full max-w-md mx-auto bg-green-50 rounded-xl shadow-xl overflow-hidden">
<div class="px-6 py-4 border-b border-green-200 flex items-center justify-between">
<h3 class="text-lg font-semibold text-green-900">Modal Title</h3>
<button class="p-2 text-green-600 hover:text-green-800" data-action="click->modals-basic#close" aria-label="Close">
<svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
</svg>
</button>
</div>
<div class="px-6 py-5">
<p class="text-green-700">This is the modal content area. Add any content here.</p>
</div>
<div class="px-6 py-4 bg-green-100 border-t border-green-200 flex justify-end space-x-3">
<button class="px-4 py-2 rounded-lg bg-green-200 text-green-800 hover:bg-green-300" data-action="click->modals-basic#close">Cancel</button>
<button class="px-4 py-2 rounded-lg bg-green-700 text-green-50 hover:bg-green-800" data-action="click->modals-basic#close">Confirm</button>
</div>
</div>
</div>
</div>
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = ["container", "panel"]
connect() {
// Bind the keydown event to close modal on Escape
this.boundKeydown = this.keydown.bind(this)
}
open() {
if (this.hasContainerTarget) {
this.containerTarget.classList.remove("hidden")
document.body.classList.add("overflow-hidden")
// Add event listener for Escape key
document.addEventListener("keydown", this.boundKeydown)
// Focus management for accessibility
setTimeout(() => {
const focusableElement = this.containerTarget.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')
if (focusableElement) {
focusableElement.focus()
}
}, 100)
}
}
close() {
if (this.hasContainerTarget) {
this.containerTarget.classList.add("hidden")
document.body.classList.remove("overflow-hidden")
// Remove event listener
document.removeEventListener("keydown", this.boundKeydown)
}
}
// Close modal when clicking the backdrop
closeOnBackdrop(event) {
if (event.target === event.currentTarget) {
this.close()
}
}
// Close modal when pressing Escape
keydown(event) {
if (event.key === "Escape") {
this.close()
}
}
disconnect() {
document.removeEventListener("keydown", this.boundKeydown)
document.body.classList.remove("overflow-hidden")
}
}
Installation & Usage
Copy the ERB template
Copy the ERB code from the template tab above and paste it into your Rails view file.
Add the Stimulus controller
Create the Stimulus controller file in your JavaScript controllers directory.