A modern Angular 19 library for creating beautiful, accessible modals with zero CSS setup required. Built with Tailwind CSS and DaisyUI styling included - just focus on your content, we handle the modal infrastructure!
- π― Angular 19 Native - Built with standalone components and modern Angular patterns
 - π¨ Zero Setup Styling - No CSS required! Pre-styled with Tailwind CSS and DaisyUI
 - β‘ Dynamic Modal Creation - Programmatically create modals with any Angular component
 - π Data Management - Bidirectional data binding between modal and content
 - βΏ Accessibility First - ARIA attributes, focus management, and keyboard navigation
 - π Event System - Comprehensive lifecycle events for modal states
 - π± Responsive Design - Works seamlessly across all devices and screen sizes
 - π Multiple Themes - Full support for all DaisyUI themes and customization
 - π Generic Modals - Pre-built confirmation, information, and input modals using Angular signals for optimal performance
 
The library follows a service-centric architecture with these core concepts:
- Service Pattern: Centralized modal management through 
NgxTailwindModalService - Built-in Styling: Complete modal styling provided - no additional CSS required
 - Stack Management: Z-index based modal layering with proper focus handling
 - Event System: Custom browser events for modal lifecycle communication
 - Instance Management: Modal wrapper class for direct component control
 - Standalone Components: Angular 19 compatible with tree-shaking support
 
npm install @dotted-labs/ngx-tailwind-modalnpm install tailwindcss daisyuiAdd the following to your tailwind.config.js to include the library's pre-built modal styles:
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./src/**/*.{html,ts}",
    "./node_modules/@dotted-labs/ngx-tailwind-modal/**/*.{js,ts}"
  ],
  // These classes provide complete modal styling - no additional CSS needed
  safelist: ['modal', 'modal-backdrop', 'modal-box'], 
  plugins: [require('daisyui')]
};// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideNgxTailwindModal } from '@dotted-labs/ngx-tailwind-modal';
export const appConfig: ApplicationConfig = {
  providers: [
    // ... other providers
    provideNgxTailwindModal()
  ]
};import { Component } from '@angular/core';
import { NgxTailwindModalComponent } from '@dotted-labs/ngx-tailwind-modal';
@Component({
  selector: 'app-root',
  standalone: true,
  imports: [NgxTailwindModalComponent],
  templateUrl: './app.component.html'
})
export class AppComponent {}π‘ No CSS Required! The library provides complete modal styling out of the box. Just focus on your content - we handle the modal backdrop, positioning, animations, and responsive design.
import { Component } from '@angular/core';
import { NgxTailwindModalViewComponent } from '@dotted-labs/ngx-tailwind-modal';
@Component({
  selector: 'app-example-modal',
  standalone: true,
  template: `
    <!-- Only content styling needed - modal structure is automatic -->
    <div class="modal-box">
      <h3 class="font-bold text-lg">Welcome!</h3>
      <p class="py-4">Focus on your content - the modal backdrop, positioning, and animations are handled automatically!</p>
      <div class="modal-action">
        <button class="btn btn-ghost" (click)="closeModal()">Cancel</button>
        <button class="btn btn-primary" (click)="confirmAction()">Confirm</button>
      </div>
    </div>
  `
})
export class ExampleModalComponent extends NgxTailwindModalViewComponent {
  closeModal() {
    this.modalInstance.close();
  }
  
  confirmAction() {
    // Perform action then close
    console.log('Action confirmed!');
    this.modalInstance.close();
  }
}import { Component, inject, ViewContainerRef } from '@angular/core';
import { NgxTailwindModalService } from '@dotted-labs/ngx-tailwind-modal';
import { ExampleModalComponent } from './example-modal.component';
@Component({
  selector: 'app-demo',
  standalone: true,
  template: `
    <div class="space-y-4 p-8">
      <button class="btn btn-primary" (click)="openSimpleModal()">
        Open Simple Modal
      </button>
      
      <button class="btn btn-secondary" (click)="openModalWithData()">
        Open Modal with Data
      </button>
    </div>
  `
})
export class DemoComponent {
  private readonly modalService = inject(NgxTailwindModalService);
  private readonly vcr = inject(ViewContainerRef);
  openSimpleModal() {
    this.modalService
      .create('example-modal', ExampleModalComponent, this.vcr, {
        dismissable: true,
        escapable: true
      })
      .open();
  }
  
  openModalWithData() {
    this.modalService
      .create('data-modal', ExampleModalComponent, this.vcr)
      .setData({ message: 'Hello from parent!', timestamp: Date.now() })
      .open();
  }
}The primary service for managing modals throughout your application.
// Create and immediately open a modal
this.modalService.create('modal-id', ComponentClass, viewContainerRef, options).open();
// Basic modal operations
this.modalService.open('modal-id');          // Open modal
this.modalService.close('modal-id');         // Close modal  
this.modalService.toggle('modal-id');        // Toggle modal state
this.modalService.closeAll();                // Close all open modals
// Data management
this.modalService.setModalData(data, 'modal-id');  // Pass data to modal
const data = this.modalService.getModalData('modal-id');  // Get modal data
this.modalService.resetModalData('modal-id'); // Clear modal data
// Modal inspection
const modal = this.modalService.getModal('modal-id');     // Get modal instance
const stack = this.modalService.getModalStack();          // Get all modals
const opened = this.modalService.getOpenedModals();       // Get open modals
const topModal = this.modalService.getTopOpenedModal();   // Get top modalinterface INgxTailwindModalOptions {
  closable?: boolean;         // Show close button (default: true)
  escapable?: boolean;        // Close on Escape key (default: true)
  dismissable?: boolean;      // Close on backdrop click (default: true)
  customClass?: string;       // Additional CSS classes
  backdrop?: boolean;         // Show backdrop (default: true)
  force?: boolean;            // Override existing modal (default: true)
  hideDelay?: number;         // Close animation delay (default: 500)
  autostart?: boolean;        // Auto-open on creation (default: false)
  target?: string;            // Position relative to element
  ariaLabel?: string;         // ARIA accessibility label
  ariaLabelledBy?: string;    // ARIA labelledby reference
  ariaDescribedBy?: string;   // ARIA describedby reference
  refocus?: boolean;          // Return focus after close (default: true)
}Extend this class in your modal content components for direct modal access:
export class YourModalComponent extends NgxTailwindModalViewComponent {
  ngOnInit() {
    console.log('Modal ID:', this.modalId);
    console.log('Modal data:', this.modalInstance.getData());
  }
  
  closeWithResult(result: any) {
    this.modalInstance.setData(result);
    this.modalInstance.close();
  }
}Available Properties:
modalId: string- Unique modal identifiermodalInstance: NgxTailwindModalComponent- Direct modal control
Listen to modal lifecycle events:
// Component-level events
modal.onOpen.subscribe(() => console.log('Modal opened'));
modal.onClose.subscribe(() => console.log('Modal closed'));
modal.onDataAdded.subscribe(data => console.log('Data added:', data));
// Global browser events
document.addEventListener('ngx-tailwind-modal.open', (e) => {
  console.log('Modal opened:', e.detail);
});Available Events:
onOpen/onOpenFinished- Modal opening lifecycleonClose/onCloseFinished- Modal closing lifecycleonDismiss/onDismissFinished- Modal dismissal (backdrop/escape)onDataAdded/onDataRemoved- Data management eventsonEscape- Escape key pressed
The library includes pre-built modal components for common use cases, accessible through simple service methods that return promises. All components use Angular signals for optimal performance and reactive state management.
Ask users to confirm or cancel actions with customizable messages and button variants.
import { NgxTailwindModalService } from '@dotted-labs/ngx-tailwind-modal';
async openConfirmation() {
  const result = await this.modalService.showConfirmation({
    title: 'Delete User',
    message: 'Are you sure you want to delete this user? This action cannot be undone.',
    confirmText: 'Delete',
    cancelText: 'Cancel',
    variant: 'danger', // success | danger | warning | info
    icon: 'warning'
  });
  if (result.confirmed) {
    // User clicked "Delete"
    this.deleteUser();
  } else {
    // User clicked "Cancel" or closed modal
    console.log('User cancelled deletion');
  }
}Configuration Options:
interface IConfirmationModalData {
  title?: string;                    // Modal title
  message: string;                   // Main message text
  confirmText?: string;              // Confirm button text (default: "Confirm")
  cancelText?: string;               // Cancel button text (default: "Cancel")
  variant?: 'success' | 'danger' | 'warning' | 'info'; // Button and icon styling
  icon?: string;                     // Optional icon display
}Display informational messages with optional auto-close functionality.
async showSuccess() {
  await this.modalService.showInfo({
    title: 'Success!',
    message: 'Your profile has been updated successfully.',
    buttonText: 'Got it',
    variant: 'success',
    icon: 'success',
    autoCloseTimer: 3000  // Auto-close after 3 seconds
  });
  console.log('Info modal was closed');
}Configuration Options:
interface IInfoModalData {
  title?: string;                    // Modal title
  message: string;                   // Main message text
  buttonText?: string;               // Button text (default: "OK")
  variant?: 'success' | 'error' | 'warning' | 'info'; // Styling variant
  icon?: string;                     // Optional icon display
  autoCloseTimer?: number;           // Auto-close timer in milliseconds
}Collect user input with built-in validation support.
async getUserInput() {
  const result = await this.modalService.showInput({
    title: 'Enter Your Name',
    message: 'Please provide your full name for the registration.',
    inputLabel: 'Full Name',
    inputPlaceholder: 'Enter your name...',
    confirmText: 'Submit',
    cancelText: 'Cancel',
    inputType: 'text',
    required: true,
    minLength: 2,
    maxLength: 50
  });
  if (!result.cancelled && result.value) {
    console.log('User entered:', result.value);
    this.processUserName(result.value);
  } else {
    console.log('User cancelled input');
  }
}Configuration Options:
interface IInputModalData {
  title?: string;                    // Modal title
  message: string;                   // Main message text
  inputLabel?: string;               // Input field label
  inputPlaceholder?: string;         // Input placeholder text
  confirmText?: string;              // Confirm button text (default: "Confirm")
  cancelText?: string;               // Cancel button text (default: "Cancel")
  inputType?: 'text' | 'email' | 'password' | 'number' | 'tel' | 'url'; // Input type
  required?: boolean;                // Whether input is required
  minLength?: number;                // Minimum input length
  maxLength?: number;                // Maximum input length
  pattern?: string;                  // RegExp pattern for validation
  defaultValue?: string;             // Default input value
}Chaining Multiple Modals:
async confirmDeleteWithInput() {
  // First, get confirmation
  const confirmation = await this.modalService.showConfirmation({
    title: 'Confirm Deletion',
    message: 'Type "DELETE" to confirm this action.',
    variant: 'danger'
  });
  if (confirmation.confirmed) {
    // Then collect confirmation text
    const input = await this.modalService.showInput({
      title: 'Final Confirmation',
      message: 'Type "DELETE" exactly to proceed:',
      inputType: 'text',
      required: true,
      pattern: '^DELETE$'
    });
    if (!input.cancelled && input.value === 'DELETE') {
      // Show success message
      await this.modalService.showInfo({
        title: 'Deleted Successfully',
        message: 'The item has been permanently deleted.',
        variant: 'success',
        autoCloseTimer: 2000
      });
    }
  }
}Custom Modal Options:
const result = await this.modalService.showConfirmation(
  {
    title: 'Custom Styled Confirmation',
    message: 'This modal uses custom options.',
    variant: 'warning'
  },
  {
    escapable: false,        // Disable escape key
    backdrop: false,         // Remove backdrop
    customClass: 'my-modal', // Add custom CSS class
    hideDelay: 200          // Faster close animation
  }
);π¨ Styling Made Easy: The library automatically provides modal backdrop, positioning, z-index management, and animations. You only need to style your content inside
modal-box.
Declare modals directly in your templates:
<!-- The library handles all modal infrastructure automatically -->
<ngx-tailwind-modal 
  identifier="settings-modal"
  [closable]="true"
  [dismissable]="true"
  [escapable]="true">
  
  <!-- Only style your content - backdrop and positioning are automatic -->
  <div class="modal-box max-w-2xl">
    <h3 class="font-bold text-lg mb-4">Application Settings</h3>
    
    <!-- Settings form content -->
    <form class="space-y-4">
      <div class="form-control">
        <label class="label">
          <span class="label-text">Theme</span>
        </label>
        <select class="select select-bordered">
          <option>Light</option>
          <option>Dark</option>
          <option>Auto</option>
        </select>
      </div>
    </form>
    
    <div class="modal-action">
      <button class="btn" (click)="closeModal()">Cancel</button>
      <button class="btn btn-primary" (click)="saveSettings()">Save Changes</button>
    </div>
  </div>
</ngx-tailwind-modal>
<!-- Open the modal using the service -->
<button class="btn btn-outline" (click)="openSettings()">
  Open Settings
</button>// Parent component passing data
export class ParentComponent {
  openUserModal(user: User) {
    this.modalService
      .create('user-modal', UserModalComponent, this.vcr)
      .setData({ user, mode: 'edit' })
      .open();
  }
}
// Modal component receiving data
export class UserModalComponent extends NgxTailwindModalViewComponent implements OnInit {
  user!: User;
  mode!: 'view' | 'edit';
  
  ngOnInit() {
    const data = this.modalInstance.getData() as { user: User, mode: string };
    this.user = data.user;
    this.mode = data.mode as 'view' | 'edit';
  }
  
  saveUser() {
    // Update user data
    this.modalInstance.setData({ user: this.user, result: 'saved' });
    this.modalInstance.close();
  }
}// Open multiple modals in sequence
openConfirmationFlow() {
  // First modal
  const userModal = this.modalService.create('user-form', UserFormComponent, this.vcr);
  
  userModal.onClose.subscribe(() => {
    // Open confirmation modal on top
    this.modalService
      .create('confirm-save', ConfirmationComponent, this.vcr, {
        backdrop: false  // Don't block the previous modal
      })
      .open();
  });
  
  userModal.open();
}π± Built-in Responsive Design: The library automatically handles responsive positioning and sizing. You only need to specify content dimensions.
// The library handles modal positioning - you only style content dimensions
@Component({
  template: `
    <!-- Library provides responsive modal backdrop and positioning -->
    <div class="modal-box w-11/12 max-w-5xl lg:max-w-3xl md:max-w-2xl sm:max-w-sm">
      <!-- Focus only on your content layout -->
      <h3 class="font-bold text-lg">Responsive Content</h3>
      <p>The modal backdrop, positioning, and z-index are handled automatically!</p>
    </div>
  `
})
export class ResponsiveModalComponent {}- ARIA Support: Full ARIA attributes for screen readers
 - Focus Management: Automatic focus trapping and restoration
 - Keyboard Navigation: Escape key, Tab navigation support
 - Semantic HTML: Proper role assignments and structure
 - Screen Reader Compatible: Optimized announcements and descriptions
 
// Example with accessibility options
this.modalService.create('accessible-modal', ContentComponent, this.vcr, {
  ariaLabel: 'User profile settings dialog',
  ariaLabelledBy: 'modal-title',
  ariaDescribedBy: 'modal-description',
  refocus: true  // Return focus to trigger element
});π No CSS Setup Required: The library automatically applies DaisyUI modal styling including backdrop, animations, and responsive behavior. You only customize the content inside
modal-box.
All DaisyUI themes work automatically without additional setup:
<!-- Apply theme at document level - modal backdrop styles automatically -->
<html data-theme="dark">
<!-- Or apply theme to specific modal content -->
<div data-theme="cyberpunk" class="modal-box">
  <!-- Only content needs theme styling - modal structure is automatic -->
  <h3 class="font-bold text-lg">Cyberpunk Modal</h3>
  <p>Backdrop and positioning are handled by the library!</p>
</div>Focus on Content: The library manages all modal infrastructure. Customize only your content styling.
// Add custom classes to your modal content
this.modalService.create('styled-modal', Component, this.vcr, {
  customClass: 'custom-content-styles'
});/* Style only your modal content - library handles the rest */
.custom-content-styles .modal-box {
  @apply bg-gradient-to-r from-purple-500 to-pink-500;
  @apply text-white shadow-xl;
  /* No need to style backdrop, positioning, or z-index */
}# Build library for production
ng build @dotted-labs/ngx-tailwind-modal
# Build library in watch mode
ng build @dotted-labs/ngx-tailwind-modal --watch --configuration development
# Run tests
ng test @dotted-labs/ngx-tailwind-modal
# Run linting
ng lint @dotted-labs/ngx-tailwind-modal
# Start demo application
ng serve demo// Test modal service in your unit tests
import { TestBed } from '@angular/core/testing';
import { NgxTailwindModalService } from '@dotted-labs/ngx-tailwind-modal';
describe('Modal Integration', () => {
  let modalService: NgxTailwindModalService;
  
  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [NgxTailwindModalService]
    });
    modalService = TestBed.inject(NgxTailwindModalService);
  });
  
  it('should create and open modal', () => {
    const modal = modalService.create('test-modal', TestComponent, vcr);
    modal.open();
    expect(modal.isVisible()).toBe(true);
  });
});projects/ngx-tailwind-modal/
βββ src/
β   βββ lib/
β   β   βββ components/
β   β   β   βββ ngx-tailwind-modal.component.ts
β   β   β   βββ ngx-tailwind-modal-view.component.ts
β   β   βββ services/
β   β   β   βββ ngx-tailwind-modal.service.ts
β   β   β   βββ ngx-tailwind-modal-stack.service.ts
β   β   βββ interfaces/
β   β   β   βββ ngx-tailwind-modal.interface.ts
β   β   βββ classes/
β   β   β   βββ modal-instance.ts
β   β   βββ providers/
β   β       βββ ngx-tailwind-modal.provider.ts
β   βββ public-api.ts
projects/demo/           # Demo application
Minimal Setup: Only Tailwind CSS and DaisyUI are required - no additional modal CSS frameworks needed!
- Angular: 19.0.0+
 - TypeScript: 5.0+
 - Tailwind CSS: 4.0+ (provides utility classes)
 - DaisyUI: 5.0+ (provides complete modal styling)
 - RxJS: 7.8+
 
What's Included: Complete modal system with backdrop, animations, positioning, focus management, and responsive design.
Explore the comprehensive demo application:
# Clone the repository
git clone https://github.com/dotted-labs/ngx-tailwind-modal.git
cd ngx-tailwind-modal
# Install dependencies
npm install
# Start demo application
ng serve demoVisit http://localhost:4200 to see live examples with:
- Multiple modal types and configurations (no CSS required!)
 - Theme switching demonstrations
 - Accessibility features showcase
 - Advanced usage patterns with zero modal styling setup
 
We welcome contributions! Please see our Contributing Guide for details.
- Fork the repository
 - Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
 
MIT License - see the LICENSE file for details.
- ngx-smart-modal - Original inspiration and architecture patterns
 - Tailwind CSS - Modern utility-first CSS framework
 - DaisyUI - Beautiful component library for Tailwind
 - Angular Team - Fantastic framework and standalone component architecture
 
β Star this repository if it helped you!