Dialog (Alert Dialog)
Dialog (Alert Dialog)
This page shows an alert modal pattern using the (role="alertdialog") ARIA attribute. A modal is a dialog box/popup window that is displayed on top of the current page and requires a user action to close it. The alert is only available to users when the modal is active. When the modal is active, the rest of the page is unavailable by mouse, keyboard, touch, or screen reader.
Turn on a screen reader to experience this example in action.
| Options | Show Dialog |
|---|---|
| Placing focus on the first focusable element within the dialog | |
| Placing focus on the dialog <h1> |
Alert dialog
This is the description for the message alert dialog
Message alert dialogs are more urgent than regular message dialogs. They can contain long (or short) passages of text.
HTML Source Code
<div>
<div>
<table class="data">
<tr>
<th>Options</th>
<th>Show Dialog</th>
</tr>
<tr>
<td>Placing focus on the first focusable element within the dialog</td>
<td><button type="button" id="show-dialog" class="primary" >Show Dialog (Option 1)</button></td>
</tr>
<tr>
<td>Placing focus on the dialog <h1> </td>
<td><button type="button" id="show-dialog-2" class="primary" >Show Dialog (Option 2)</button></td>
</tr>
</table>
</div>
</div>
<div class="dialog-container" id="dialog-1-container">
<div role="alertdialog" class="dialog" aria-labelledby="dialog-1-title" aria-describedby="dialog-1-desc" id="dialog-1" aria-modal="true" >
<h1 class="dialog-title" id="dialog-1-title">Alert dialog</h1>
<hr/>
<p id="dialog-1-desc" >This is the description for the message alert dialog<br/>
Message alert dialogs are more urgent than regular message dialogs. They can contain long (or short) passages of text.</p>
<hr/>
<div class="dialog-action">
<button type="button" id="dialog-btn-submit" class="primary" >Continue</button>
<button type="button" id="dialog-btn-cancel" class="cancel-button" >Cancel</button>
</div>
<button class="close-button" aria-label="close dialog" id="dialog-btn-close"><i class="fa-solid fa-xmark"></i></button>
</div>
</div>
<!---
This component has been adapted from an example provided by the W3C, in accordance with the W3C Software and Document License https://www.w3.org/copyright/software-license-2023/
-->
JavaScript Source Code
var dialog, dialog2;
window.addEventListener('load', function () {
var options = [];
dialog = new Dialog(document.getElementById('dialog-1-container'), options);
options = [];
options["type"] = USE_TABINDEX;
dialog2 = new Dialog(document.getElementById('dialog-1-container'), options);
document.getElementById('show-dialog').addEventListener('click', showDialog);
document.getElementById('show-dialog-2').addEventListener('click', showDialogWithLOption2);
document.getElementById('dialog-btn-submit').addEventListener('click', processSubmit);
});
function showDialog(event)
{
dialog.show(event.target);
}
function showDialogWithLOption2()
{
dialog2.show(event.target);
}
var langText = {
"submitMessage": "Submitted data will be processed and the dialog will close."
};
function processSubmit()
{
alert(langText["submitMessage"]);
if (dialog.open) dialog.hide();
if (dialog2.open) dialog2.hide();
}
const USE_FIRST_ELEMENT_FOCUS = 1;
const USE_TABINDEX = 2;
class Dialog {
constructor(container, options )
{
var self=this;
this.type = USE_FIRST_ELEMENT_FOCUS;
this.triggeredBy = null;
this.open = false;
this.bodyHandler = null;
if (typeof options == "undefined") options = [];
if (!Array.isArray(options)) options = [];
if(typeof options["type"] === "number")
{
var val = options["type"];
if( (val == USE_FIRST_ELEMENT_FOCUS) || (val == USE_TABINDEX))
this.type = val;
}
if(!isDOMElement(container))
{
console.log("container parameter should be DOM HTML element object.");
return;
}
this.container = container;
container.addEventListener("keydown", function(event) { self.handleEscapeKey(event, self);});
var dialog = container.querySelector(".dialog");
if(!isDOMElement(dialog))
{
console.log("dialog not found inside the container provided.");
return;
}
this.dialog = dialog;
var btnCancel = container.querySelector(".cancel-button");
var btnClose = container.querySelector(".close-button");
if(btnCancel) btnCancel.addEventListener('click', function() { self.hide();} );
if(btnClose) btnClose.addEventListener('click', function() { self.hide();});
var focusElements = dialog.querySelectorAll('a[href], button, input, textarea, select, details, [tabindex]:not([tabindex="-1"])');
if(focusElements.length > 0)
{
var firstFocusElement = focusElements[0];
var lastFocusElement = focusElements[focusElements.length-1];
self.firstFocusElement = firstFocusElement;
self.lastFocusElement = lastFocusElement;
lastFocusElement.addEventListener("keydown", function(event){
var isTabPressed = event.key === 'Tab';
if(isTabPressed && !event.shiftKey)
{
event.preventDefault();
firstFocusElement.focus();
}
});
firstFocusElement.addEventListener("keydown", function(event){
var isTabPressed = event.key === 'Tab';
if(isTabPressed && event.shiftKey )
{
event.preventDefault();
lastFocusElement.focus();
}
});
}
}
handleEscapeKey(event, dialog)
{
if(event.key == "Escape")
dialog.hide();
}
handleOutsideFocus(event, dialog )
{
var isTabPressed = event.key === 'Tab';
if(isTabPressed && event.shiftKey && (event.target.tagName.toLowerCase() == "body") )
{
event.preventDefault();
dialog.lastFocusElement.focus();
}
if(event.key == "Escape")
dialog.hide();
}
show(element)
{
if(isDOMElement(element))
this.triggeredBy = element;
if(!this.container.classList.contains("show"))
this.container.classList.add("show");
if(this.type == USE_FIRST_ELEMENT_FOCUS)
this.firstFocusElement.focus();
if(this.type == USE_TABINDEX)
{
var title = this.dialog.querySelector(".dialog-title");
if(title)
{
title.setAttribute('tabindex', "-1");
setTimeout(function(){ title.focus(); },50);
}
else
console.log("title element not specified or does not have '.dialog-title' class added.");
}
var self = this;
var fn = function(event) { self.handleOutsideFocus(event, self); }
this.bodyHandler = fn;
document.body.addEventListener("keydown", fn );
this.open = true;
this.hideAllSiblings();
}
hide()
{
if(!this.open) return;
if(this.container.classList.contains("show"))
this.container.classList.remove("show");
if(this.triggeredBy )
this.triggeredBy.focus();
if(this.bodyHandler)
document.body.removeEventListener("keydown", this.bodyHandler );
this.open = false;
this.unhideAllSiblings();
}
hideAllSiblings()
{
if(!isDOMElement(this.container)) return;
var ancestor = null;
var parent = this.container;
var level = 0;
setTimeout(function(){
do
{
ancestor = parent;
parent = parent.parentNode;
level++;
var siblings = parent.childNodes;
siblings.forEach(function(child){
if( typeof child.tagName == "undefined") return;
var tagName = child.tagName.toLowerCase();
if( (tagName == "script") || (tagName == "style")) return;
if(!ancestor.isSameNode(child))
child.setAttribute("aria-hidden", "true");
});
}
while ( (level > 100) || (parent.tagName.toLowerCase() != "body"));
}, 100);
}
unhideAllSiblings()
{
if(!isDOMElement(this.container)) return;
var ancestor = null;
var parent = this.container;
var level = 0;
do
{
ancestor = parent;
parent = parent.parentNode;
level++;
var siblings = parent.childNodes;
siblings.forEach(function(child){
if( typeof child.tagName == "undefined") return;
var tagName = child.tagName.toLowerCase();
if( (tagName == "script") || (tagName == "style")) return;
if(!ancestor.isSameNode(child) && child.hasAttribute("aria-hidden") )
child.removeAttribute( "aria-hidden");
});
}
while ( (level > 100) || (parent.tagName.toLowerCase() != "body"));
}
}// of Dialog class
function isDOMElement(element)
{
if ( (typeof element === "undefined") || (typeof element !== "object"))
return false;
if( element instanceof HTMLElement)
return true;
if(element.nodeType === 1 && typeof element.nodeName==="string")
return true;
return false;
}
//This component has been adapted from an example provided by the W3C, in accordance with the W3C Software and Document License https://www.w3.org/copyright/software-license-2023/
CSS Source Code
/*
Dialog (Simple Dialog) — Restyled
Deque University ARIA Component
This component has been adapted from an example provided by the W3C,
in accordance with the W3C Software and Document License
https://www.w3.org/copyright/software-license-2023/
*/
:root {
--dqu-interactive: #2e5f7a;
--dqu-interactive-hover: #3a7a9a;
--dqu-interactive-light: rgba(46, 95, 122, 0.08);
--dqu-bg-primary: #fcfaf8;
--dqu-bg-secondary: #f6f3ed;
--dqu-border-secondary: #8c827d;
--dqu-text-primary: #21201e;
--dqu-font-family: "Noto Sans", sans-serif;
--dqu-radius: 8px;
--dqu-error: #9e2a2b;
}
/* Overlay */
.dialog-container {
position: fixed;
display: none;
overflow-y: auto;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1;
background: rgb(0 0 0 / 40%);
}
.show {
display: block;
}
/* Dialog box */
.dialog {
padding: 24px;
border: 1px solid var(--dqu-border-secondary);
border-radius: var(--dqu-radius);
width: 40%;
top: 5rem;
left: 25%;
position: absolute;
background-color: var(--dqu-bg-primary);
font-family: var(--dqu-font-family);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
}
/* Title */
.dialog-title {
text-align: left !important;
font-size: 1.25rem !important;
font-weight: 600 !important;
width: calc(100% - 30px);
margin: 0 0 0 3px !important;
padding: 0 !important;
color: var(--dqu-text-primary) !important;
font-family: var(--dqu-font-family) !important;
border: none !important;
}
hr {
border: none;
border-top: 1px solid var(--dqu-border-secondary);
margin: 16px 0;
}
/* Form */
.dialog-form {
padding: 8px 0;
}
.row {
display: flex;
margin: 10px 0;
align-items: center;
}
.label {
width: 30%;
text-align: right;
margin-right: 15px;
font-family: var(--dqu-font-family);
font-size: 0.875rem;
font-weight: 500;
color: var(--dqu-text-primary);
}
.dqu-example input[type="text"],
.dqu-example textarea {
padding: 8px 12px !important;
height: auto !important;
border: 1px solid var(--dqu-border-secondary) !important;
border-radius: var(--dqu-radius) !important;
font-family: var(--dqu-font-family) !important;
font-size: 1rem !important;
background: var(--dqu-bg-primary) !important;
color: var(--dqu-text-primary) !important;
box-sizing: border-box !important;
}
.dqu-example input[type="text"]:hover,
.dqu-example textarea:hover {
border-color: var(--dqu-border-secondary) !important;
outline: none !important;
}
.dqu-example input[type="text"]:focus,
.dqu-example textarea:focus {
outline: 2px solid var(--dqu-interactive) !important;
outline-offset: 2px;
border: 1px solid var(--dqu-interactive) !important;
}
.extend {
width: 100%;
box-sizing: border-box;
}
/* Buttons */
.dialog-action {
display: flex;
justify-content: center;
gap: 16px;
}
.dqu-example button {
padding: 8px 14px;
font-family: var(--dqu-font-family);
font-size: 0.875rem;
font-weight: 600;
margin: 4px;
border-radius: 9999px;
cursor: pointer;
border: 1px solid var(--dqu-border-secondary);
background: var(--dqu-bg-primary);
color: var(--dqu-text-primary);
transition: background-color 0.15s ease;
}
/* Larger buttons inside the dialog */
.dialog-action button {
padding: 10px 16px;
font-size: 1rem;
}
/* Table styles */
table.data {
border-collapse: collapse;
font-family: var(--dqu-font-family);
font-size: 0.9375rem;
}
table.data th,
table.data td {
border: 1px solid var(--dqu-border-secondary);
padding: 10px 12px;
text-align: left;
color: var(--dqu-text-primary);
}
table.data th {
background: var(--dqu-bg-secondary);
font-weight: 600;
}
.dqu-example button:hover {
background-color: var(--dqu-interactive-light);
outline: 2px solid var(--dqu-interactive) !important;
outline-offset: 2px;
}
.dqu-example button:focus {
outline: 3px solid var(--dqu-interactive) !important;
outline-offset: 2px;
}
/* Scoped to .dqu-example so specificity exceeds the scoped `.dqu-example button`
base rule (which would otherwise win on `<button class="primary">`). */
.dqu-example .primary {
background-color: var(--dqu-interactive);
color: #ffffff;
border-color: var(--dqu-interactive);
}
.dqu-example .primary:hover {
background-color: var(--dqu-interactive-hover);
}
/* Unscoped so primary buttons render correctly in the library, where the
MODX template adds no .dqu-example or .example ancestor wrapper. */
button.primary {
background-color: var(--dqu-interactive) !important;
color: #ffffff !important;
border: 1px solid var(--dqu-interactive) !important;
border-radius: 9999px !important;
padding: 8px 14px !important;
font-family: var(--dqu-font-family) !important;
font-size: 0.875rem !important;
font-weight: 600 !important;
cursor: pointer;
}
button.primary:hover {
background-color: var(--dqu-interactive-hover) !important;
outline: 2px solid var(--dqu-interactive) !important;
outline-offset: 2px;
}
button.primary:focus {
background-color: var(--dqu-interactive-hover) !important;
outline: 3px solid var(--dqu-interactive) !important;
outline-offset: 2px;
}
.dqu-example .cancel-button {
background-color: rgba(194, 162, 112, 0.2);
color: var(--dqu-text-primary);
border-color: var(--dqu-border-secondary);
}
.dqu-example .cancel-button:hover,
.dqu-example .cancel-button:focus {
background-color: var(--dqu-interactive-light) !important;
}
/* Unscoped — library context has no .dqu-example ancestor */
button.cancel-button {
background-color: rgba(194, 162, 112, 0.2) !important;
color: var(--dqu-text-primary) !important;
border: 1px solid var(--dqu-border-secondary) !important;
border-radius: 9999px !important;
padding: 8px 14px !important;
font-family: var(--dqu-font-family) !important;
font-size: 0.875rem !important;
font-weight: 600 !important;
cursor: pointer;
}
button.cancel-button:hover,
button.cancel-button:focus {
background-color: var(--dqu-interactive-light) !important;
outline: 2px solid var(--dqu-interactive) !important;
outline-offset: 2px;
}
button.cancel-button:focus {
outline-width: 3px;
}
.dialog .close-button {
position: absolute !important;
top: 12px;
right: 12px;
background-color: rgba(194, 162, 112, 0.2) !important;
color: var(--dqu-text-primary);
border: 1px solid var(--dqu-border-secondary);
font-size: 18px;
width: 32px;
height: 32px;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
border-radius: 9999px;
margin: 0;
}
.dialog .close-button:hover {
background-color: var(--dqu-interactive-light) !important;
outline: 2px solid var(--dqu-interactive) !important;
outline-offset: 2px;
}
.dialog .close-button:focus {
outline: 3px solid var(--dqu-interactive) !important;
outline-offset: 2px;
background-color: var(--dqu-interactive-light) !important;
}
.icon {
font-size: 18px;
}
/* Responsive */
@media screen and (max-width: 1000px) {
.dialog-form { padding: 5px; }
}
@media screen and (max-width: 720px) {
.row {
display: flex;
flex-direction: column;
margin: 10px;
align-items: flex-start;
}
.label {
width: 100%;
text-align: left;
margin-right: 0;
margin-bottom: 4px;
}
.dialog {
width: 70%;
left: 10%;
}
}