a11y: queue progress bar — role=progressbar + aria-valuenow

The download progress bar inside each queue item was a plain
<div class="queue-progress-bar" style="width: 73%;"> with no
semantic indication that it represented progress. Screen readers
just announced the surrounding text ("Downloading...") with no
running value.

Added role="progressbar" + aria-valuemin=0 + aria-valuemax=100 +
aria-valuenow on the wrapping .queue-progress-wrap (since the bar
itself is just the visual fill — the wrap is the semantic gauge
region). aria-label is the status label so AT announces "VOD title
75 percent" instead of an unlabeled gauge.

updateQueueItemProgress also re-stamps aria-valuenow as the
percentage advances, so AT live regions can pick up the running
update without needing a full re-render.

For indeterminate progress (pre-rolling, before the first byte
event arrives) aria-valuenow stays at 0 — the screen reader still
gets a coherent reading even if visually the bar is in
indeterminate-pulse mode.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
xRangerDE 2026-05-11 04:27:13 +02:00
parent c6ae0cadbd
commit 58e52399c5

View File

@ -412,6 +412,7 @@ function updateQueueItemProgress(progress: DownloadProgress): void {
if (!item) return;
const bar = el.querySelector('.queue-progress-bar') as HTMLElement | null;
const wrap = el.querySelector('.queue-progress-wrap') as HTMLElement | null;
const text = el.querySelector('.queue-progress-text') as HTMLElement | null;
const meta = el.querySelector('.queue-meta') as HTMLElement | null;
@ -420,6 +421,7 @@ function updateQueueItemProgress(progress: DownloadProgress): void {
const pct = isDeterminate ? Math.min(100, progress.progress) : 0;
bar.style.width = `${pct}%`;
bar.className = `queue-progress-bar${isDeterminate ? '' : ' indeterminate'}`;
if (wrap) wrap.setAttribute('aria-valuenow', String(Math.round(pct)));
}
if (text) text.textContent = getQueueProgressText(item);
if (meta) meta.textContent = getQueueMetaText(item);
@ -559,7 +561,7 @@ function renderQueue(): void {
<div class="queue-status-label">${safeStatusLabel}</div>
</div>
<div class="queue-meta">${safeMeta}${mergeMetaExtra}</div>
<div class="queue-progress-wrap">
<div class="queue-progress-wrap" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="${Math.round(progressValue)}" aria-label="${escapeHtml(safeStatusLabel)}">
<div class="queue-progress-bar${progressClass}" style="width: ${progressValue}%;"></div>
</div>
<div class="queue-progress-text">${safeProgressText}</div>