SuperModal

v1.0.0 · Last updated April 2026

Accessible, animated modals with HTMX-powered dynamic content loading. Complete documentation of all configuration options and patterns.

# Installation

SuperModal is included in the AZ UI Kit bundle.

HTML
<link rel="stylesheet" href="https://cdn.tunet.dev/az-ui-kit/1.0.0/az-ui-kit.min.css">
<script src="https://cdn.tunet.dev/az-ui-kit/1.0.0/az-ui-kit.min.js"></script>

# Basic Usage

Create a modal with a trigger button and modal content.

HTML
<!-- Trigger Button -->
<button data-az-modal-trigger="my-modal">
  Open Modal
</button>

<!-- Modal -->
<div data-az-component="super-modal"
     data-az-modal-id="my-modal">
  
  <!-- Backdrop -->
  <div data-az-modal-backdrop></div>
  
  <!-- Dialog -->
  <div data-az-modal-dialog>
    <!-- Header -->
    <div data-az-modal-header>
      <h2>Modal Title</h2>
      <button data-az-modal-close>×</button>
    </div>
    
    <!-- Body -->
    <div data-az-modal-body>
      <p>Modal content goes here.</p>
    </div>
    
    <!-- Footer -->
    <div data-az-modal-footer>
      <button data-az-modal-close>Cancel</button>
      <button data-az-modal-action="confirm">Confirm</button>
    </div>
  </div>
</div>

# Data Attributes Reference

Modal Container Attributes

Attribute Type Default Description
data-az-component string Must be "super-modal"
data-az-modal-id string required Unique identifier for the modal
data-az-modal-size string "md" "sm" | "md" | "lg" | "xl" | "full"
data-az-modal-animation string "fade" "fade" | "slide" | "scale" | "none"
data-az-modal-position string "center" "center" | "top" | "right" | "bottom" | "left"
data-az-modal-closable boolean true Allow closing via backdrop/escape
data-az-modal-persistent boolean false Keep content in DOM when closed
data-az-modal-stack boolean true Allow nested/stacked modals
data-az-modal-scroll string "body" "body" | "dialog" — scrollable area
data-az-modal-focus-trap boolean true Trap keyboard focus within modal
data-az-modal-auto-focus string "first" Selector or "first" | "none"

Trigger Attributes

Attribute Description
data-az-modal-trigger Modal ID to open
data-az-modal-close Close the current modal (no value needed)
data-az-modal-action Action name to emit on click ("confirm", "delete", etc.)

Part Attributes (semantic regions)

Attribute Description
data-az-modal-backdrop Background overlay element
data-az-modal-dialog Main dialog container
data-az-modal-header Header region (title, close button)
data-az-modal-body Main content area
data-az-modal-footer Footer region (actions)

# Sizes

Pre-defined modal sizes for common use cases.

Size Max Width Use Case
sm 400px Confirmations, alerts, simple inputs
md 560px Forms, content previews (default)
lg 800px Complex forms, tables, wizards
xl 1140px Dashboards, reports, media viewers
full 100vw - 2rem Full-screen experiences
HTML
<div data-az-component="super-modal"
     data-az-modal-id="large-modal"
     data-az-modal-size="lg">
  ...
</div>

# Animations

Built-in animation presets with customizable duration and easing.

fade

Simple opacity transition (default)

data-az-modal-animation="fade"

slide

Slides in from the position direction

data-az-modal-animation="slide"

scale

Scales up from center with fade

data-az-modal-animation="scale"

none

Instant show/hide without animation

data-az-modal-animation="none"

Custom Animation Timing

HTML
<div data-az-component="super-modal"
     data-az-modal-id="custom-animation"
     data-az-modal-animation="scale"
     data-az-modal-duration="300"
     data-az-modal-easing="cubic-bezier(0.16, 1, 0.3, 1)">
  ...
</div>

# HTMX Integration

Load modal content dynamically from the server.

Load Content on Open

HTML
<!-- Trigger with dynamic content -->
<button data-az-modal-trigger="user-modal"
        hx-get="/users/123/profile"
        hx-target="#user-modal [data-az-modal-body]"
        hx-trigger="click">
  View Profile
</button>

<!-- Modal with placeholder -->
<div data-az-component="super-modal"
     data-az-modal-id="user-modal"
     data-az-modal-loading="true">
  <div data-az-modal-backdrop></div>
  <div data-az-modal-dialog>
    <div data-az-modal-header>
      <h2>User Profile</h2>
      <button data-az-modal-close>×</button>
    </div>
    <div data-az-modal-body>
      <!-- Loading state shown while fetching -->
      <div data-az-modal-loader>
        Loading...
      </div>
    </div>
  </div>
</div>

Submit Forms Inside Modal

HTML
<div data-az-modal-body>
  <form hx-post="/users/create"
        hx-swap="none"
        data-az-modal-form>
    <input name="name" placeholder="Name">
    <input name="email" placeholder="Email">
    
    <button type="submit">Create User</button>
  </form>
</div>

<script>
// Close modal on successful form submission
document.addEventListener('htmx:afterRequest', (e) => {
  if (e.detail.successful && e.target.hasAttribute('data-az-modal-form')) {
    SuperModal.close();
  }
});
</script>

Dynamic Modal Content Response

Server Response (Django)
def profile_modal(request, user_id):
    user = get_object_or_404(User, pk=user_id)
    # Return partial HTML
    return render(request, 'partials/user_profile.html', {'user': user})

# Nested Modals

Stack modals on top of each other with proper z-index and focus management.

HTML
<!-- Parent Modal -->
<div data-az-component="super-modal"
     data-az-modal-id="parent-modal"
     data-az-modal-stack="true">
  <div data-az-modal-dialog>
    <div data-az-modal-body>
      <p>This is the parent modal.</p>
      
      <!-- Trigger for nested modal -->
      <button data-az-modal-trigger="confirm-modal">
        Open Confirmation
      </button>
    </div>
  </div>
</div>

<!-- Nested Modal (Confirmation) -->
<div data-az-component="super-modal"
     data-az-modal-id="confirm-modal"
     data-az-modal-size="sm">
  <div data-az-modal-dialog>
    <div data-az-modal-body>
      <p>Are you sure?</p>
    </div>
    <div data-az-modal-footer>
      <button data-az-modal-close>Cancel</button>
      <button data-az-modal-action="confirm">Confirm</button>
    </div>
  </div>
</div>

Note: When closing nested modals, focus automatically returns to the parent modal. Pressing Escape closes only the topmost modal.

# Events

Event Description Detail
az:modal:open Before modal opens (cancelable) { modalId }
az:modal:opened After modal fully opened { modalId }
az:modal:close Before modal closes (cancelable) { modalId, trigger }
az:modal:closed After modal fully closed { modalId }
az:modal:action Action button clicked { modalId, action }

Usage Example

JavaScript
// Prevent closing with unsaved changes
document.addEventListener('az:modal:close', (e) => {
  if (e.detail.modalId === 'edit-form' && hasUnsavedChanges()) {
    e.preventDefault();
    showConfirmation();
  }
});

// Handle action buttons
document.addEventListener('az:modal:action', (e) => {
  if (e.detail.action === 'confirm') {
    submitForm();
    SuperModal.close(e.detail.modalId);
  }
});

// Analytics
document.addEventListener('az:modal:opened', (e) => {
  analytics.track('modal_opened', { modal: e.detail.modalId });
});

# Accessibility

SuperModal follows WAI-ARIA Dialog patterns.

Focus Trap

Tab key cycles through focusable elements within the modal

Focus Restoration

Focus returns to trigger element when modal closes

Escape to Close

Pressing Escape closes the modal (configurable)

ARIA Attributes

Automatic role="dialog", aria-modal, aria-labelledby

Background Inert

Content behind modal is marked inert for screen readers

Labeling Best Practices

HTML
<div data-az-component="super-modal"
     data-az-modal-id="accessible-modal"
     aria-labelledby="modal-title"
     aria-describedby="modal-description">
  <div data-az-modal-dialog>
    <div data-az-modal-header>
      <h2 id="modal-title">Edit Profile</h2>
    </div>
    <div data-az-modal-body>
      <p id="modal-description">
        Update your profile information below.
      </p>
      <!-- Form fields -->
    </div>
  </div>
</div>