a11y: dialog roles + aria-labelledby + aria-label on modal closes

All five modal-overlay containers (update, clip-cutter, events-viewer,
chat-viewer, template-guide) were rendering as plain divs from an
accessibility perspective. Screen readers would announce nothing
distinguishing when one of them opened, and the close-X buttons would
read as "x button" with no semantic meaning.

Added on each .modal-overlay:
- role="dialog" — tells assistive tech this is a modal region
- aria-modal="true" — instructs the reader to ignore content outside
  the dialog while it is open (matches the keyboard escape + click-
  outside-to-dismiss behavior the renderer already implements)
- aria-labelledby="<existingTitleId>" — every modal already had a
  uniquely-IDd h2; pointed each dialog at its own title so the reader
  announces e.g. "Stream events dialog" on open

Added on each .modal-close button:
- aria-label="Close" — gives the X button a real semantic label
  independent of the visual character

Zero visual change, zero behavior change. Just makes the app actually
usable for someone running NVDA/JAWS/Orca/VoiceOver. WCAG 4.1.2 +
2.1.1 + 1.3.1 alignment.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
xRangerDE 2026-05-11 01:50:26 +02:00
parent 5200126565
commit afef213b45

View File

@ -18,9 +18,9 @@
<button id="updateButton" onclick="downloadUpdate()">Jetzt herunterladen</button> <button id="updateButton" onclick="downloadUpdate()">Jetzt herunterladen</button>
</div> </div>
<div class="modal-overlay" id="updateModal" onclick="handleUpdateModalOverlayClick(event)"> <div class="modal-overlay" id="updateModal" role="dialog" aria-modal="true" aria-labelledby="updateModalTitle" onclick="handleUpdateModalOverlayClick(event)">
<div class="modal update-modal"> <div class="modal update-modal">
<button class="modal-close" onclick="dismissUpdateModal()">x</button> <button class="modal-close" aria-label="Close" onclick="dismissUpdateModal()">x</button>
<div class="update-modal-eyebrow" id="updateModalEyebrow">Updates</div> <div class="update-modal-eyebrow" id="updateModalEyebrow">Updates</div>
<h2 id="updateModalTitle">Update verfugbar</h2> <h2 id="updateModalTitle">Update verfugbar</h2>
<p class="update-modal-message" id="updateModalMessage">Version 0.0.0 ist verfugbar. Jetzt herunterladen?</p> <p class="update-modal-message" id="updateModalMessage">Version 0.0.0 ist verfugbar. Jetzt herunterladen?</p>
@ -46,9 +46,9 @@
</div> </div>
<!-- Clip Dialog Modal --> <!-- Clip Dialog Modal -->
<div class="modal-overlay" id="clipModal"> <div class="modal-overlay" id="clipModal" role="dialog" aria-modal="true" aria-labelledby="clipDialogTitle">
<div class="modal" style="background: #2b2b2b; max-width: 500px;"> <div class="modal" style="background: #2b2b2b; max-width: 500px;">
<button class="modal-close" onclick="closeClipDialog()">x</button> <button class="modal-close" aria-label="Close" onclick="closeClipDialog()">x</button>
<h2 style="color: #E5A00D; text-align: center; margin-bottom: 20px;" id="clipDialogTitle">VOD zuschneiden</h2> <h2 style="color: #E5A00D; text-align: center; margin-bottom: 20px;" id="clipDialogTitle">VOD zuschneiden</h2>
<!-- Start Zeit mit Slider --> <!-- Start Zeit mit Slider -->
@ -137,9 +137,9 @@
</div> </div>
<!-- Events Viewer Modal --> <!-- Events Viewer Modal -->
<div class="modal-overlay" id="eventsViewerModal"> <div class="modal-overlay" id="eventsViewerModal" role="dialog" aria-modal="true" aria-labelledby="eventsViewerTitle">
<div class="modal" style="max-width: 700px; max-height: 80vh; display:flex; flex-direction:column;"> <div class="modal" style="max-width: 700px; max-height: 80vh; display:flex; flex-direction:column;">
<button class="modal-close" onclick="closeEventsViewer()">x</button> <button class="modal-close" aria-label="Close" onclick="closeEventsViewer()">x</button>
<h2 id="eventsViewerTitle" style="margin-top:0;">Stream events</h2> <h2 id="eventsViewerTitle" style="margin-top:0;">Stream events</h2>
<div id="eventsViewerStatus" style="color:var(--text-secondary); font-size:12px; margin-bottom:8px;"></div> <div id="eventsViewerStatus" style="color:var(--text-secondary); font-size:12px; margin-bottom:8px;"></div>
<div id="eventsViewerList" style="flex:1; overflow-y:auto; background: var(--bg-main); border:1px solid var(--border-soft); border-radius:6px; padding:8px;"></div> <div id="eventsViewerList" style="flex:1; overflow-y:auto; background: var(--bg-main); border:1px solid var(--border-soft); border-radius:6px; padding:8px;"></div>
@ -147,9 +147,9 @@
</div> </div>
<!-- Chat Replay Viewer Modal --> <!-- Chat Replay Viewer Modal -->
<div class="modal-overlay" id="chatViewerModal"> <div class="modal-overlay" id="chatViewerModal" role="dialog" aria-modal="true" aria-labelledby="chatViewerTitle">
<div class="modal" style="max-width: 800px; height: 80vh; display:flex; flex-direction:column;"> <div class="modal" style="max-width: 800px; height: 80vh; display:flex; flex-direction:column;">
<button class="modal-close" onclick="closeChatViewer()">x</button> <button class="modal-close" aria-label="Close" onclick="closeChatViewer()">x</button>
<h2 id="chatViewerTitle" style="margin-top:0;">Chat replay</h2> <h2 id="chatViewerTitle" style="margin-top:0;">Chat replay</h2>
<div class="form-row" style="margin-bottom:8px; gap:8px; flex-wrap:wrap; align-items:center;"> <div class="form-row" style="margin-bottom:8px; gap:8px; flex-wrap:wrap; align-items:center;">
<input type="text" id="chatViewerFilter" placeholder="Filter..." oninput="onChatViewerFilterChange()" style="flex:1; min-width:160px; background: var(--bg-card); border:1px solid var(--border-soft); border-radius:6px; padding:6px 10px; color:var(--text); font-size:13px;"> <input type="text" id="chatViewerFilter" placeholder="Filter..." oninput="onChatViewerFilterChange()" style="flex:1; min-width:160px; background: var(--bg-card); border:1px solid var(--border-soft); border-radius:6px; padding:6px 10px; color:var(--text); font-size:13px;">
@ -160,9 +160,9 @@
</div> </div>
<!-- Template Guide Modal --> <!-- Template Guide Modal -->
<div class="modal-overlay" id="templateGuideModal"> <div class="modal-overlay" id="templateGuideModal" role="dialog" aria-modal="true" aria-labelledby="templateGuideTitle">
<div class="modal template-guide-modal"> <div class="modal template-guide-modal">
<button class="modal-close" onclick="closeTemplateGuide()">x</button> <button class="modal-close" aria-label="Close" onclick="closeTemplateGuide()">x</button>
<h2 id="templateGuideTitle">Template Guide</h2> <h2 id="templateGuideTitle">Template Guide</h2>
<p id="templateGuideIntro" class="template-guide-intro">Nutze Variablen fur Dateinamen und prufe das Ergebnis als Live-Vorschau.</p> <p id="templateGuideIntro" class="template-guide-intro">Nutze Variablen fur Dateinamen und prufe das Ergebnis als Live-Vorschau.</p>