a11y: queue-item title + selector keyboard-accessible

Two more click-only divs in the queue item template were leaving
keyboard users stuck:

- .queue-selector — the "X" number-badge to the left of pending
  queue items that toggles bulk-select. Previously a div with onclick.
  Now role="checkbox" + tabindex + aria-checked tracking the selection
  state + Enter/Space keydown handler.

- .queue-item .title — the truncated VOD title that, when clicked,
  toggles the expanded detail panel underneath the row. Previously
  a div with onclick. Now role="button" + tabindex +
  aria-expanded reflecting the panel state + aria-controls pointing
  at the details panel ID + Enter/Space keydown handler.

Both pick up 2px purple focus-visible rings to match the rest of
the a11y family.

aria-expanded on a button is the conventional pattern for
"disclosure widget" controls (collapsible/expandable content),
so screen readers will now announce the title as "VOD title,
button, collapsed" or "expanded" as the user navigates and toggles.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
xRangerDE 2026-05-11 03:46:40 +02:00
parent e95be22a02
commit 3e37d780c3
2 changed files with 13 additions and 2 deletions

View File

@ -549,13 +549,13 @@ function renderQueue(): void {
return ` return `
<div class="queue-item${isMergeGroup ? ' merge-group' : ''}" draggable="${item.status === 'pending' ? 'true' : 'false'}" data-id="${item.id}"> <div class="queue-item${isMergeGroup ? ' merge-group' : ''}" draggable="${item.status === 'pending' ? 'true' : 'false'}" data-id="${item.id}">
${showSelector ${showSelector
? `<div class="queue-selector${isSelected ? ' selected' : ''}" onclick="toggleQueueSelection('${item.id}')">${isSelected ? selectionIndex + 1 : ''}</div>` ? `<div class="queue-selector${isSelected ? ' selected' : ''}" role="checkbox" tabindex="0" aria-checked="${isSelected ? 'true' : 'false'}" onclick="toggleQueueSelection('${item.id}')" onkeydown="if(event.key==='Enter'||event.key===' '){event.preventDefault();toggleQueueSelection('${item.id}');}">${isSelected ? selectionIndex + 1 : ''}</div>`
: '' : ''
} }
<div class="status ${item.status}"></div> <div class="status ${item.status}"></div>
<div class="queue-main"> <div class="queue-main">
<div class="queue-title-row"> <div class="queue-title-row">
<div class="title" title="${safeTitle}" onclick="toggleQueueDetails('${item.id}')">${liveBadge}${healthBadge}${mergeIcon}${isClip}${safeTitle}</div> <div class="title" title="${safeTitle}" role="button" tabindex="0" aria-expanded="${expandedQueueIds.has(item.id) ? 'true' : 'false'}" aria-controls="details-${item.id}" onclick="toggleQueueDetails('${item.id}')" onkeydown="if(event.key==='Enter'||event.key===' '){event.preventDefault();toggleQueueDetails('${item.id}');}">${liveBadge}${healthBadge}${mergeIcon}${isClip}${safeTitle}</div>
<div class="queue-status-label">${safeStatusLabel}</div> <div class="queue-status-label">${safeStatusLabel}</div>
</div> </div>
<div class="queue-meta">${safeMeta}${mergeMetaExtra}</div> <div class="queue-meta">${safeMeta}${mergeMetaExtra}</div>

View File

@ -1054,6 +1054,17 @@ select option {
transition: all 0.15s; transition: all 0.15s;
} }
.queue-selector:focus-visible {
outline: none;
box-shadow: 0 0 0 2px rgba(145, 70, 255, 0.55);
}
.queue-item .title:focus-visible {
outline: none;
border-radius: 3px;
box-shadow: 0 0 0 2px rgba(145, 70, 255, 0.45);
}
.queue-selector.selected { .queue-selector.selected {
background: var(--accent); background: var(--accent);
border-color: var(--accent); border-color: var(--accent);