fix(a11y): Dialog-Vorrang in Fokusfalle, eindeutige Dialog-IDs, Bookmark nur Enter
- settings.js: _makeTrap bricht ab, wenn ein .dialog-overlay offen ist, damit der Dialog-keydown-Handler Escape/Tab allein behandelt (kein Doppelschluss, Fokusfalle bleibt dicht) - dialog.js: aria-labelledby/-describedby zeigen auf instanz-eindeutige IDs (Date.now + Modul-Zaehler) statt feste dialogTitle/dialogBody, damit kurz gestapelte Dialoge dem Screenreader nicht den falschen Titel liefern - boards.js: Bookmark-keydown reagiert bei role=link nur noch auf Enter, Space entfernt (Space ist Button-Semantik)
This commit is contained in:
+4
-3
@@ -271,10 +271,11 @@ function bindBoardListEvents(list, board) {
|
||||
}
|
||||
});
|
||||
|
||||
// Tastatur: Enter oeffnet den Bookmark wie ein Klick. Delete-Button bleibt
|
||||
// ein echter <button> und feuert seinen eigenen Klick ueber Space/Enter.
|
||||
// Tastatur: Enter oeffnet den Bookmark wie ein Klick. role="link" erwartet
|
||||
// nur Enter (Space ist Button-Semantik). Der Delete-Button bleibt ein echter
|
||||
// <button> und feuert seinen eigenen Klick ueber Space/Enter selbst.
|
||||
list.addEventListener('keydown', e => {
|
||||
if (e.key !== 'Enter' && e.key !== ' ') return;
|
||||
if (e.key !== 'Enter') return;
|
||||
const bmItem = e.target.closest('.bm-item');
|
||||
if (!bmItem || e.target !== bmItem) return; // nur wenn der li selbst fokussiert ist
|
||||
e.preventDefault();
|
||||
|
||||
+8
-4
@@ -48,22 +48,26 @@ const HellionDialog = {
|
||||
box.className = 'dialog-box';
|
||||
box.setAttribute('role', config.isConfirm ? 'alertdialog' : 'dialog');
|
||||
box.setAttribute('aria-modal', 'true');
|
||||
box.setAttribute('aria-labelledby', 'dialogTitle');
|
||||
box.setAttribute('aria-describedby', 'dialogBody');
|
||||
// Eindeutige IDs pro Dialog-Instanz: kurz gestapelte Dialoge (timer.js/
|
||||
// image-ref.js feuern teils ohne await) duerfen sich keine festen IDs
|
||||
// teilen, sonst liest der Screenreader ueber aria-* den falschen Titel.
|
||||
const uid = 'dlg-' + Date.now().toString(36) + '-' + (HellionDialog._seq = (HellionDialog._seq || 0) + 1);
|
||||
box.setAttribute('aria-labelledby', uid + '-title');
|
||||
box.setAttribute('aria-describedby', uid + '-body');
|
||||
|
||||
// Header
|
||||
const header = document.createElement('div');
|
||||
header.className = 'dialog-header';
|
||||
header.appendChild(this._createIcon(config.type));
|
||||
const titleSpan = document.createElement('span');
|
||||
titleSpan.id = 'dialogTitle';
|
||||
titleSpan.id = uid + '-title';
|
||||
titleSpan.textContent = config.title;
|
||||
header.appendChild(titleSpan);
|
||||
|
||||
// Body
|
||||
const body = document.createElement('div');
|
||||
body.className = 'dialog-body';
|
||||
body.id = 'dialogBody';
|
||||
body.id = uid + '-body';
|
||||
body.textContent = config.message;
|
||||
|
||||
// Actions
|
||||
|
||||
@@ -18,6 +18,10 @@ function _focusable(container) {
|
||||
/** Tab/Shift+Tab im Container einfangen + Escape schliesst. */
|
||||
function _makeTrap(container, closeFn) {
|
||||
return function trap(e) {
|
||||
// Ein offener HellionDialog (z.B. Reset-All-Confirm oder BG-URL-Alert aus
|
||||
// dem Panel) hat Vorrang: sein eigener keydown-Handler uebernimmt Escape/Tab.
|
||||
// Sonst schloessen beide Listener gleichzeitig und die Dialog-Fokusfalle wird loechrig.
|
||||
if (document.querySelector('.dialog-overlay')) return;
|
||||
if (e.key === 'Escape') { e.preventDefault(); closeFn(); return; }
|
||||
if (e.key !== 'Tab') return;
|
||||
const items = _focusable(container);
|
||||
|
||||
Reference in New Issue
Block a user