cleanup+a11y: VOD select checkbox — proper aria-label + CSS-class styling

The bulk-select checkbox on each VOD card was carrying a ~140 char
inline-style block (absolute positioning, dimensions, accent-color,
z-index, cursor) — duplicated across every rendered VOD — plus a
truly bizarre title-attribute fallback that hacked the
bulkSelectedCount locale string by stripping placeholder digits:

  title=`${UI_TEXT.vods.bulkSelectedCount
                .replace("{count}", "0")
                .replace(/[0-9]/g, "")
                .trim() || "Select"}`

That worked by accident — the placeholder happens to be "{count}
ausgewahlt" / "{count} selected" so stripping digits gave a usable
fragment — but it was fragile and not really an accessible label.

Three fixes:
- Extracted the inline styles to a .vod-select-checkbox CSS rule.
  The custom checkbox styling from 4.6.26 means accent-color was
  redundant anyway, so dropping it is a no-op visually.
- Added a proper locale key vods.selectAriaLabel ("Select VOD for
  bulk action" / "VOD fuer Bulk-Aktion auswaehlen") for the
  aria-label attribute.
- Dropped the title-attribute hack entirely. aria-label now provides
  the AT-readable name; sighted users get the visual checkbox
  which is self-explanatory.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
xRangerDE 2026-05-11 05:24:50 +02:00
parent e56bac2c2b
commit 69b83c9d22
4 changed files with 16 additions and 1 deletions

View File

@ -363,6 +363,7 @@ const UI_TEXT_DE = {
updateProgressAria: 'Update-Download-Fortschritt'
},
vods: {
selectAriaLabel: 'VOD fuer Bulk-Aktion auswaehlen',
noneTitle: 'Keine VODs',
noneText: 'Wahle einen Streamer aus der Liste.',
loading: 'Lade VODs...',

View File

@ -363,6 +363,7 @@ const UI_TEXT_EN = {
updateProgressAria: 'Update download progress'
},
vods: {
selectAriaLabel: 'Select VOD for bulk action',
noneTitle: 'No VODs',
noneText: 'Select a streamer from the list.',
loading: 'Loading VODs...',

View File

@ -257,7 +257,7 @@ function buildVodCardHtml(vod: VOD, streamer: string, downloadedIds?: Set<string
data-vod-date="${safeDateAttr}"
data-vod-streamer="${safeStreamerAttr}"
data-vod-duration="${safeDurationAttr}">
<input type="checkbox" class="vod-select-checkbox" data-vod-url="${safeUrlAttr}" ${isChecked ? 'checked' : ''} title="${escapeHtml(UI_TEXT.vods.bulkSelectedCount.replace('{count}', '0').replace(/[0-9]/g, '').trim() || 'Select')}" style="position:absolute; top:8px; left:8px; width:18px; height:18px; accent-color:#9146FF; cursor:pointer; z-index:2;">
<input type="checkbox" class="vod-select-checkbox" data-vod-url="${safeUrlAttr}" ${isChecked ? 'checked' : ''} aria-label="${escapeHtml(UI_TEXT.vods.selectAriaLabel)}">
${downloadedBadge}
<div class="vod-thumb-wrap">
<img class="vod-thumbnail" loading="lazy" decoding="async" src="${thumb}" alt="" title="${escapeHtml(UI_TEXT.vods.openOnTwitch)}" onerror="this.src='data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 320 180%22><rect fill=%22%23333%22 width=%22320%22 height=%22180%22/></svg>'">

View File

@ -1318,6 +1318,19 @@ select option {
box-shadow: 0 0 0 3px rgba(145, 70, 255, 0.35);
}
/* The bulk-select checkbox overlaid on each VOD thumbnail top-left.
Positioned absolutely so it sits over the artwork without affecting
the cards flex/info layout. */
.vod-select-checkbox {
position: absolute;
top: 8px;
left: 8px;
width: 18px;
height: 18px;
cursor: pointer;
z-index: 2;
}
.vod-card.selected {
box-shadow: 0 0 0 2px #9146FF, 0 8px 25px rgba(145, 70, 255, 0.25);
}