Refactor admin modals: replace backdrop divs with native <dialog> elements for improved accessibility and functionality

This commit is contained in:
David Dyer committed 2026-10-25 13:08:40 -04:00
1 parent 242c5165e1
commit 02551eebbc
8 files changed
+123 -106

No files matched your search

+11
View File
@@ -42,3 +42,14 @@ document.addEventListener('submit', (e) => {
e.preventDefault();
}
});
// Announcement editor dialog: the server flags it for ?new / ?edit URLs.
// Closing it any way (× link, Esc) drops the query string, so a reload
// doesn't reopen it.
const annModal = document.getElementById('modal-ann');
if (annModal?.hasAttribute('data-open-on-load')) {
annModal.showModal();
annModal.addEventListener('close', () => {
history.replaceState(null, '', 'index.php');
});
}
+5 -7
View File
@@ -239,11 +239,10 @@ $announcements = get_all_announcements();
</div>
</main>
<!-- Modal Backdrop: Announcement Editor (Create / Edit) — server-rendered
open/closed via the is-open class, since this page has no JS file;
the underlying save is still a plain form POST + reload, unchanged. -->
<div class="admin-modal-backdrop<?= ($isCreatingNew || $editItem) ? ' is-open' : '' ?>" role="dialog" aria-modal="true" aria-labelledby="modal-ann-title">
<div class="admin-modal">
<!-- Modal: Announcement Editor (Create / Edit). The server marks it
data-open-on-load for ?new / ?edit URLs and index.js opens it with
showModal(); saving is still a plain form POST + reload. -->
<dialog class="admin-modal" id="modal-ann" aria-labelledby="modal-ann-title"<?= ($isCreatingNew || $editItem) ? ' data-open-on-load' : '' ?>>
<div class="modal-header">
<h2 id="modal-ann-title"><?= $editItem ? '✏️ Edit Announcement' : '✨ New Announcement' ?></h2>
<a href="index.php" class="modal-close" aria-label="Close dialog">&times;</a>
@@ -317,8 +316,7 @@ $announcements = get_all_announcements();
<a href="index.php" class="btn btn-outline">Cancel</a>
</div>
</form>
</div>
</div>
</dialog>
<script src="index.js" defer></script>
+6 -6
View File
@@ -5,8 +5,8 @@
const csrfToken = document.body.dataset.csrfToken;
// Item Modal helpers
const itemModal = document.getElementById('modal-item-backdrop');
const catModal = document.getElementById('modal-cat-backdrop');
const itemModal = document.getElementById('modal-item');
const catModal = document.getElementById('modal-cat');
function openItemModal(itemData = null) {
const title = document.getElementById('modal-item-title');
@@ -37,12 +37,12 @@ function openItemModal(itemData = null) {
availField.checked = true;
}
itemModal.classList.add('is-open');
itemModal.showModal();
nameField.focus();
}
function closeItemModal() {
itemModal.classList.remove('is-open');
itemModal.close();
}
function openCategoryModal(catData = null) {
@@ -63,12 +63,12 @@ function openCategoryModal(catData = null) {
descField.value = '';
}
catModal.classList.add('is-open');
catModal.showModal();
nameField.focus();
}
function closeCategoryModal() {
catModal.classList.remove('is-open');
catModal.close();
}
function escapeHtml(text) {
+6 -10
View File
@@ -328,9 +328,8 @@ $menuEnabled = is_menu_enabled();
</div>
</main>
<!-- Modal Backdrop: Menu Item -->
<div class="admin-modal-backdrop" id="modal-item-backdrop" role="dialog" aria-modal="true" aria-labelledby="modal-item-title">
<div class="admin-modal">
<!-- Modal: Menu Item -->
<dialog class="admin-modal" id="modal-item" aria-labelledby="modal-item-title">
<div class="modal-header">
<h2 id="modal-item-title">✨ Add Menu Item</h2>
<button type="button" class="modal-close" aria-label="Close dialog" id="btn-close-item-modal">&times;</button>
@@ -383,12 +382,10 @@ $menuEnabled = is_menu_enabled();
<button type="button" class="btn btn-outline" id="btn-cancel-item">Cancel</button>
</div>
</form>
</div>
</div>
</dialog>
<!-- Modal Backdrop: Category -->
<div class="admin-modal-backdrop" id="modal-cat-backdrop" role="dialog" aria-modal="true" aria-labelledby="modal-cat-title">
<div class="admin-modal">
<!-- Modal: Category -->
<dialog class="admin-modal" id="modal-cat" aria-labelledby="modal-cat-title">
<div class="modal-header">
<h2 id="modal-cat-title">✨ Add Category</h2>
<button type="button" class="modal-close" aria-label="Close dialog" id="btn-close-cat-modal">&times;</button>
@@ -415,8 +412,7 @@ $menuEnabled = is_menu_enabled();
<button type="button" class="btn btn-outline" id="btn-cancel-cat">Cancel</button>
</div>
</form>
</div>
</div>
</dialog>
<script src="menu.js" defer></script>
+10 -12
View File
@@ -5,9 +5,9 @@
const csrfToken = document.body.dataset.csrfToken;
const currentUserId = document.body.dataset.currentUserId;
const userModal = document.getElementById('modal-user-backdrop');
const passwordModal = document.getElementById('modal-password-backdrop');
const editModal = document.getElementById('modal-edit-backdrop');
const userModal = document.getElementById('modal-user');
const passwordModal = document.getElementById('modal-password');
const editModal = document.getElementById('modal-edit');
function escapeHtml(text) {
const div = document.createElement('div');
@@ -54,16 +54,14 @@ function openUserModal() {
const form = document.getElementById('form-user');
form.reset();
resetPasswordVisibility(form);
userModal.classList.add('is-open');
userModal.showModal();
}
// Safari ignores autocomplete="off" on fields labeled "Username" and offers
// saved logins whenever one has focus. The modals used to auto-focus that
// field, and a closed modal left it focused, so the prompt kept popping up.
// Now nothing is auto-focused, and closing a modal drops focus.
// Native <dialog>: close() hides it (not rendered, so its fields can't keep
// focus or trigger autofill) and returns focus to whatever opened it.
// Esc closes it too, with no extra code.
function closeModal(modal) {
modal.classList.remove('is-open');
if (modal.contains(document.activeElement)) document.activeElement.blur();
modal.close();
}
function closeUserModal() {
@@ -84,7 +82,7 @@ function openPasswordModal(userId, username) {
document.getElementById('password-current-group').style.display = isSelf ? '' : 'none';
passwordModal.classList.add('is-open');
passwordModal.showModal();
(isSelf ? document.getElementById('password-field-current') : document.getElementById('password-field-new')).focus();
}
@@ -105,7 +103,7 @@ function openEditModal(userId, username, role) {
roleSelect.disabled = isSelf;
document.getElementById('edit-role-self-hint').hidden = !isSelf;
editModal.classList.add('is-open');
editModal.showModal();
}
function closeEditModal() {
+9 -15
View File
@@ -302,9 +302,8 @@ $users = get_all_users();
</div>
</main>
<!-- Modal Backdrop: Add User -->
<div class="admin-modal-backdrop" id="modal-user-backdrop" role="dialog" aria-modal="true" aria-labelledby="modal-user-title">
<div class="admin-modal">
<!-- Modal: Add User -->
<dialog class="admin-modal" id="modal-user" aria-labelledby="modal-user-title">
<div class="modal-header">
<h2 id="modal-user-title">✨ Add User</h2>
<button type="button" class="modal-close" aria-label="Close dialog" id="btn-close-user-modal">&times;</button>
@@ -361,12 +360,10 @@ $users = get_all_users();
<button type="button" class="btn btn-outline" id="btn-cancel-user">Cancel</button>
</div>
</form>
</div>
</div>
</dialog>
<!-- Modal Backdrop: Edit User -->
<div class="admin-modal-backdrop" id="modal-edit-backdrop" role="dialog" aria-modal="true" aria-labelledby="modal-edit-title">
<div class="admin-modal">
<!-- Modal: Edit User -->
<dialog class="admin-modal" id="modal-edit" aria-labelledby="modal-edit-title">
<div class="modal-header">
<h2 id="modal-edit-title">✏️ Edit User</h2>
<button type="button" class="modal-close" aria-label="Close dialog" id="btn-close-edit-modal">&times;</button>
@@ -398,12 +395,10 @@ $users = get_all_users();
<button type="button" class="btn btn-outline" id="btn-cancel-edit">Cancel</button>
</div>
</form>
</div>
</div>
</dialog>
<!-- Modal Backdrop: Change Password -->
<div class="admin-modal-backdrop" id="modal-password-backdrop" role="dialog" aria-modal="true" aria-labelledby="modal-password-title">
<div class="admin-modal">
<!-- Modal: Change Password -->
<dialog class="admin-modal" id="modal-password" aria-labelledby="modal-password-title">
<div class="modal-header">
<h2 id="modal-password-title">🔑 Change Password</h2>
<button type="button" class="modal-close" aria-label="Close dialog" id="btn-close-password-modal">&times;</button>
@@ -459,8 +454,7 @@ $users = get_all_users();
<button type="button" class="btn btn-outline" id="btn-cancel-password">Cancel</button>
</div>
</form>
</div>
</div>
</dialog>
<script src="users.js" defer></script>