Modal
Modal dialogs for focused user interactions. Includes backdrop, focus trap, and keyboard navigation.
Basic Modal
A modal dialog with header, body, and footer. Click backdrop or press ESC to close.
<!-- Trigger button -->
<button data-ui-toggle="modal" data-ui-target="#myModal">
Open Modal
</button>
<!-- Modal structure -->
<div id="myModal" class="ui-modal">
<div class="ui-modal__backdrop" data-ui-dismiss="modal"></div>
<div class="ui-modal__dialog">
<div class="ui-modal__content">
<div class="ui-modal__header">
<h5 class="ui-modal__title">Modal Title</h5>
<button class="ui-modal__close" data-ui-dismiss="modal">×</button>
</div>
<div class="ui-modal__body">
Modal content goes here.
</div>
<div class="ui-modal__footer">
<button class="ui-btn ui-btn--secondary" data-ui-dismiss="modal">Cancel</button>
<button class="ui-btn ui-btn--primary">Save</button>
</div>
</div>
</div>
</div>
<!-- Trigger button -->
<button data-ui-toggle="modal"
data-ui-target="#myModal"
ng-click="onOpenModal()">
{{ openButtonLabel }}
</button>
<!-- Modal structure -->
<div id="myModal" class="ui-modal">
<div class="ui-modal__backdrop" data-ui-dismiss="modal"></div>
<div class="ui-modal__dialog">
<div class="ui-modal__content">
<div class="ui-modal__header">
<h5 class="ui-modal__title">{{ modalTitle }}</h5>
<button class="ui-modal__close" data-ui-dismiss="modal">×</button>
</div>
<div class="ui-modal__body">
{{ modalContent }}
</div>
<div class="ui-modal__footer">
<button class="ui-btn ui-btn--secondary" data-ui-dismiss="modal">Cancel</button>
<button class="ui-btn ui-btn--primary" ng-click="onSave()">Save</button>
</div>
</div>
</div>
</div>
<!-- Trigger button -->
<button data-ui-toggle="modal"
data-ui-target="#myModal"
(click)="onOpenModal()">
{{ openButtonLabel }}
</button>
<!-- Modal structure -->
<div id="myModal" class="ui-modal">
<div class="ui-modal__backdrop" data-ui-dismiss="modal"></div>
<div class="ui-modal__dialog">
<div class="ui-modal__content">
<div class="ui-modal__header">
<h5 class="ui-modal__title">{{ modalTitle }}</h5>
<button class="ui-modal__close" data-ui-dismiss="modal">×</button>
</div>
<div class="ui-modal__body">
{{ modalContent }}
</div>
<div class="ui-modal__footer">
<button class="ui-btn ui-btn--secondary" data-ui-dismiss="modal">Cancel</button>
<button class="ui-btn ui-btn--primary" (click)="onSave()">Save</button>
</div>
</div>
</div>
</div>
{/* Trigger button */}
<button data-ui-toggle="modal"
data-ui-target="#myModal"
onClick={handleOpenModal}>
{openButtonLabel}
</button>
{/* Modal structure */}
<div id="myModal" className="ui-modal">
<div className="ui-modal__backdrop" data-ui-dismiss="modal"></div>
<div className="ui-modal__dialog">
<div className="ui-modal__content">
<div className="ui-modal__header">
<h5 className="ui-modal__title">{modalTitle}</h5>
<button className="ui-modal__close" data-ui-dismiss="modal">×</button>
</div>
<div className="ui-modal__body">
{modalContent}
</div>
<div className="ui-modal__footer">
<button className="ui-btn ui-btn--secondary" data-ui-dismiss="modal">Cancel</button>
<button className="ui-btn ui-btn--primary" onClick={handleSave}>Save</button>
</div>
</div>
</div>
</div>
Modal Sizes
Different sizes for various content needs.
<!-- Small -->
<div class="ui-modal__dialog ui-modal__dialog--sm">...</div>
<!-- Default (no modifier) -->
<div class="ui-modal__dialog">...</div>
<!-- Large -->
<div class="ui-modal__dialog ui-modal__dialog--lg">...</div>
<!-- Fullscreen -->
<div class="ui-modal__dialog ui-modal__dialog--fullscreen">...</div>
Static Backdrop
Prevent closing when clicking backdrop. Use for important dialogs.
<div id="staticModal" class="ui-modal" data-ui-backdrop-static>
<!-- Cannot be closed by clicking backdrop -->
</div>
Centered Modal
Vertically center the modal in the viewport.
<div class="ui-modal__dialog ui-modal__dialog--centered">
...
</div>
Scrollable Content
For long content, the modal body becomes scrollable.
<div class="ui-modal__dialog ui-modal__dialog--scrollable">
<div class="ui-modal__content">
<div class="ui-modal__body">
<!-- Long content here -->
</div>
</div>
</div>
Programmatic Control
Open and close modals via JavaScript.
import { showModal, hideModal } from '@wilout/ui-js';
// Open modal
showModal('#myModal');
// Close modal
hideModal('#myModal');
// Listen to events
document.addEventListener('ui:modal:shown', (e) => {
console.log('Modal opened');
});
document.addEventListener('ui:modal:hidden', (e) => {
console.log('Modal closed');
});
// In your controller
angular.module('app').controller('ModalCtrl', function($scope, $document) {
// Import showModal/hideModal from ui-js
var uiJs = window.WiloutUI; // or import if using module bundler
$scope.openModal = function() {
uiJs.showModal('#myModal');
};
$scope.closeModal = function() {
uiJs.hideModal('#myModal');
};
// Listen to events
$document[0].addEventListener('ui:modal:shown', function(e) {
$scope.$apply(function() {
$scope.isModalOpen = true;
console.log('Modal opened');
});
});
$document[0].addEventListener('ui:modal:hidden', function(e) {
$scope.$apply(function() {
$scope.isModalOpen = false;
console.log('Modal closed');
});
});
});
import { Component, OnInit, OnDestroy } from '@angular/core';
import { showModal, hideModal } from '@wilout/ui-js';
@Component({ ... })
export class ModalComponent implements OnInit, OnDestroy {
isModalOpen = false;
private shownHandler = () => { this.isModalOpen = true; console.log('Modal opened'); };
private hiddenHandler = () => { this.isModalOpen = false; console.log('Modal closed'); };
openModal() {
showModal('#myModal');
}
closeModal() {
hideModal('#myModal');
}
ngOnInit() {
document.addEventListener('ui:modal:shown', this.shownHandler);
document.addEventListener('ui:modal:hidden', this.hiddenHandler);
}
ngOnDestroy() {
document.removeEventListener('ui:modal:shown', this.shownHandler);
document.removeEventListener('ui:modal:hidden', this.hiddenHandler);
}
}
import { useEffect, useState, useCallback } from 'react';
import { showModal, hideModal } from '@wilout/ui-js';
function ModalExample() {
const [isModalOpen, setIsModalOpen] = useState(false);
const openModal = useCallback(() => showModal('#myModal'), []);
const closeModal = useCallback(() => hideModal('#myModal'), []);
useEffect(() => {
const handleShown = () => { setIsModalOpen(true); console.log('Modal opened'); };
const handleHidden = () => { setIsModalOpen(false); console.log('Modal closed'); };
document.addEventListener('ui:modal:shown', handleShown);
document.addEventListener('ui:modal:hidden', handleHidden);
return () => {
document.removeEventListener('ui:modal:shown', handleShown);
document.removeEventListener('ui:modal:hidden', handleHidden);
};
}, []);
return ( /* ... */ );
}
Events
| Event | Description |
|---|---|
ui:modal:show |
Fired before modal opens (cancelable) |
ui:modal:shown |
Fired after modal is fully visible |
ui:modal:hide |
Fired before modal closes (cancelable) |
ui:modal:hidden |
Fired after modal is fully hidden |
Data Attributes
| Attribute | Description |
|---|---|
data-ui-toggle="modal" |
Marks element as modal trigger |
data-ui-target="#id" |
Target modal selector |
data-ui-dismiss="modal" |
Closes the modal when clicked |
data-ui-backdrop-static |
Prevents closing on backdrop click |
Accessibility
- Focus is trapped inside the modal when open
- Press Escape to close (unless static backdrop)
- ARIA attributes (
role="dialog",aria-modal="true") are automatically added - Focus returns to trigger element when closed
- Body scroll is locked when modal is open