Two examples below. Try opening one, then use Tab / Shift+Tab
to confirm focus stays inside the dialog, and Esc to close it. Focus returns
to whichever button opened the dialog.
The minimum markup needed. data-modal marks the dialog, data-modal-open / data-modal-close wire up triggers and close buttons automatically - no JS required beyond loading the script.
<button data-modal-open="basic-modal">Open basic modal</button>
<div id="basic-modal" data-modal>
<div class="accessible-modal-box">
<button class="accessible-modal-close" data-modal-close aria-label="Close">×</button>
<h2>Basic modal</h2>
<p>Some content.</p>
</div>
</div>
<script src="accessible-modal.js"></script>
Tab through the fields - focus wraps from the last field back to the close button, and never escapes into the page behind it.
const modalEl = document.getElementById('form-modal');
const modal = new AccessibleModal(modalEl, {
initialFocus: '#demo-name'
});
document.getElementById('open-form-modal')
.addEventListener('click', (e) => modal.open(e.currentTarget));
This dialog has role="dialog", aria-modal="true", and aria-labelledby pointing at this heading - all set automatically by the script.
Press Esc, click outside the box, or use the close button.