JSFiddle - React, Tailwind, and code Playground
by ShaCP
HTML
<button onclick="openDialog('dialog1', this)">
Add Delivery Address
</button>
<div role="dialog"
id="dialog1"
aria-labelledby="dialog1_label"
aria-modal="true"
class="hidden">
<h2 id="dialog1_label" class="dialog_label">
Add Delivery Address
</h2>
<div class="dialog_form">
<div class="dialog_form_item">
<label>
<span class="label_text">
Street:
</span>
<input type="text" class="wide_input">
</label>
</div>
<div class="dialog_form_item">
<label>
<span class="label_text">
City:
</span>
<input type="text" class="city_input">
</label>
</div>
<div class="dialog_form_item">
<label>
<span class="label_text">
State:
</span>
<input type="text" class="state_input">
</label>
</div>
<div class="dialog_form_item">
<label>
<span class="label_text">
Zip:
</span>
<input type="text" class="zip_input">
</label>
</div>
<div class="dialog_form_item">
<label for="special_instructions">
<span class="label_text">
Special instructions:
</span>
</label>
<input id="special_instructions"
type="text"
aria-describedby="special_instructions_desc"
class="wide_input">
<div class="label_info" id="special_instructions_desc">
For example, gate code or other information to help the driver find you
</div>
</div>
</div>
<div class="dialog_form_actions">
<button onclick="openDialog('dialog2', this, 'dialog2_para1')">
Verify Address
</button>
<button onclick="replaceDialog('dialog3', undefined, 'dialog3_close_btn')">
Add
</button>
<button onclick="closeDialog(this)">
Cancel
</button>
</div>
</div>
<!-- Second modal to open on top of the first modal -->
<div id="dialog2"
role="dialog"
...
CSS
.hidden {
display: none;
}
[role="alertdialog"],
[role="dialog"] {
box-sizing: border-box;
padding: 15px;
border: 1px solid #000;
background-color: #fff;
min-height: 100vh;
}
@media screen and (min-width: 640px) {
[role="alertdialog"],
[role="dialog"] {
position: absolute;
top: 2rem;
left: 50vw; /* move to the middle of the screen (assumes relative parent is the body/viewport) */
transform: translateX(-50%); /* move backwards 50% of this element's width */
min-width: calc(640px - (15px * 2)); /* == breakpoint - left+right margin */
min-height: auto;
box-shadow: 0 19px 38px rgba(0, 0, 0, 0.12), 0 15px 12px rgba(0, 0, 0, 0.22);
}
}
.dialog_label {
text-align: center;
}
.dialog_form {
margin: 15px;
}
.dialog_form .label_text {
box-sizing: border-box;
padding-right: 0.5em;
display: inline-block;
font-size: 16px;
font-weight: bold;
width: 30%;
text-align: right;
}
.dialog_form .label_info {
box-sizing: border-box;
padding-right: 0.5em;
font-size: 12px;
width: 30%;
text-align: right;
display: inline-block;
}
.dialog_form_item {
margin: 10px 0;
font-size: 0;
}
.dialog_form_item .wide_input {
box-sizing: border-box;
max-width: 70%;
width: 27em;
}
.dialog_form_item .city_input {
box-sizing: border-box;
max-width: 70%;
width: 17em;
}
.dialog_form_item .state_input {
box-sizing: border-box;
max-width: 70%;
width: 15em;
}
.dialog_form_item .zip_input {
box-sizing: border-box;
max-width: 70%;
width: 9em;
}
.dialog_form_actions {
text-align: right;
padding: 0 20px 20px;
}
.dialog_close_button {
float: right;
position: absolute;
top: 10px;
left: 92%;
height: 25px;
}
.dialog_close_button img {
border: 0;
}
.dialog_desc {
padding: 10px 20px;
}
/* native <dialog> element uses the ::backdrop pseudo-element */
/* dialog::backdrop, */
.dialog-backdrop {
display: none;
position: fixed;
overflow-y: auto;
top: 0;
right: 0;
bottom: 0;
...
JavaScript
/*
* This content is licensed according to the W3C Software License at
* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
*/
var aria = aria || {};
aria.Utils = aria.Utils || {};
(function () {
/*
* When util functions move focus around, set this true so the focus listener
* can ignore the events.
*/
aria.Utils.IgnoreUtilFocusChanges = false;
aria.Utils.dialogOpenClass = 'has-dialog';
/**
* @desc Set focus on descendant nodes until the first focusable element is
* found.
* @param element
* DOM node for which to find the first focusable descendant.
* @returns
* true if a focusable element is found and focus is set.
*/
aria.Utils.focusFirstDescendant = function (element) {
for (var i = 0; i < element.childNodes.length; i++) {
var child = element.childNodes[i];
if (aria.Utils.attemptFocus(child) ||
aria.Utils.focusFirstDescendant(child)) {
return true;
}
}
return false;
}; // end focusFirstDescendant
/**
* @desc Find the last descendant node that is focusable.
* @param element
* DOM node for which to find the last focusable descendant.
* @returns
* true if a focusable element is found and focus is set.
*/
aria.Utils.focusLastDescendant = function (element) {
for (var i = element.childNodes.length - 1; i >= 0; i--) {
var child = element.childNodes[i];
if (aria.Utils.attemptFocus(child) ||
aria.Utils.focusLastDescendant(child)) {
return true;
}
}
return false;
}; // end focusLastDescendant
/**
* @desc Set Attempt to set focus on the current node.
* @param element
* The node to attempt to focus on.
* @returns
* true if element is focused.
*/
aria.Utils.attemptFocus = function (element) {
if (!aria.Utils.isFocusable(element)) {
return false;
}
aria.Utils.IgnoreUtilFocusChanges = true;
try {
...