a11y: aria-label on the 3 icon-only buttons in the main shell
Three icon-only buttons in the always-visible UI had no programmatic accessible name:
- the "+" add-streamer button (no id, no title, no aria-label — entire button was unnamed)
- the bulk-remove X button next to the Streamer section title (had a localized title, but title is not a reliable accessible-name source — many screen readers don't expose it)
- the VOD-filter clear X button above the VOD grid (same — title-only)
For all three the visible text is just a glyph ("+", "x"), so the accessible name has to come from somewhere else. Added:
- new locale key static.streamerAddAriaLabel ("Streamer hinzufuegen" / "Add streamer") and an id on the "+" button so renderer-texts can localize it
- new setAriaLabel(id, value) helper in renderer-texts mirroring the existing setTitle/setPlaceholder pattern
- aria-label calls for all three buttons, in addition to the existing title (so the tooltip stays for sighted users)
The two existing X buttons reuse their existing title strings as aria-label — no new translation work, just exposing the already-present text via the right ARIA attribute.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2df8d72a61
commit
3ec88a7800
@ -248,7 +248,7 @@
|
|||||||
<div class="header-actions">
|
<div class="header-actions">
|
||||||
<div class="header-search">
|
<div class="header-search">
|
||||||
<input type="text" id="newStreamer" placeholder="Streamer hinzufugen..." onkeypress="if(event.key==='Enter')addStreamer()">
|
<input type="text" id="newStreamer" placeholder="Streamer hinzufugen..." onkeypress="if(event.key==='Enter')addStreamer()">
|
||||||
<button onclick="addStreamer()">+</button>
|
<button id="btnAddStreamer" type="button" onclick="addStreamer()" aria-label="Add streamer" title="Add streamer">+</button>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn-icon" onclick="refreshVODs()">
|
<button class="btn-icon" onclick="refreshVODs()">
|
||||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg>
|
||||||
|
|||||||
@ -177,6 +177,7 @@ const UI_TEXT_DE = {
|
|||||||
downloadPathNotWritable: 'Download-Ordner ist nicht beschreibbar. Waehle einen anderen Ordner oder pruefe die Schreibrechte.',
|
downloadPathNotWritable: 'Download-Ordner ist nicht beschreibbar. Waehle einen anderen Ordner oder pruefe die Schreibrechte.',
|
||||||
streamerSectionTitle: 'Streamer',
|
streamerSectionTitle: 'Streamer',
|
||||||
streamerListFilterPlaceholder: 'Filtern...',
|
streamerListFilterPlaceholder: 'Filtern...',
|
||||||
|
streamerAddAriaLabel: 'Streamer hinzufuegen',
|
||||||
streamerBulkRemoveTitle: 'Alle entfernen (oder gefilterte)',
|
streamerBulkRemoveTitle: 'Alle entfernen (oder gefilterte)',
|
||||||
streamerBulkRemoveAll: 'Alle {count} Streamer aus der Liste entfernen?',
|
streamerBulkRemoveAll: 'Alle {count} Streamer aus der Liste entfernen?',
|
||||||
streamerBulkRemoveFiltered: 'Die {count} passenden Streamer aus der Liste entfernen?',
|
streamerBulkRemoveFiltered: 'Die {count} passenden Streamer aus der Liste entfernen?',
|
||||||
|
|||||||
@ -177,6 +177,7 @@ const UI_TEXT_EN = {
|
|||||||
downloadPathNotWritable: 'Download folder is not writable. Pick another folder or grant write permission.',
|
downloadPathNotWritable: 'Download folder is not writable. Pick another folder or grant write permission.',
|
||||||
streamerSectionTitle: 'Streamer',
|
streamerSectionTitle: 'Streamer',
|
||||||
streamerListFilterPlaceholder: 'Filter...',
|
streamerListFilterPlaceholder: 'Filter...',
|
||||||
|
streamerAddAriaLabel: 'Add streamer',
|
||||||
streamerBulkRemoveTitle: 'Remove all (or filtered)',
|
streamerBulkRemoveTitle: 'Remove all (or filtered)',
|
||||||
streamerBulkRemoveAll: 'Remove all {count} streamers from the list?',
|
streamerBulkRemoveAll: 'Remove all {count} streamers from the list?',
|
||||||
streamerBulkRemoveFiltered: 'Remove the {count} matching streamer(s) from the list?',
|
streamerBulkRemoveFiltered: 'Remove the {count} matching streamer(s) from the list?',
|
||||||
|
|||||||
@ -42,6 +42,11 @@ function setTitle(id: string, value: string): void {
|
|||||||
if (node) node.setAttribute('title', value);
|
if (node) node.setAttribute('title', value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setAriaLabel(id: string, value: string): void {
|
||||||
|
const node = document.getElementById(id);
|
||||||
|
if (node) node.setAttribute('aria-label', value);
|
||||||
|
}
|
||||||
|
|
||||||
function setLanguage(lang: string): LanguageCode {
|
function setLanguage(lang: string): LanguageCode {
|
||||||
currentLanguage = lang === 'en' ? 'en' : 'de';
|
currentLanguage = lang === 'en' ? 'en' : 'de';
|
||||||
UI_TEXT = UI_TEXTS[currentLanguage];
|
UI_TEXT = UI_TEXTS[currentLanguage];
|
||||||
@ -176,6 +181,9 @@ function applyLanguageToStaticUI(): void {
|
|||||||
setText('streamerSectionTitleText', UI_TEXT.static.streamerSectionTitle);
|
setText('streamerSectionTitleText', UI_TEXT.static.streamerSectionTitle);
|
||||||
setPlaceholder('streamerListFilter', UI_TEXT.static.streamerListFilterPlaceholder);
|
setPlaceholder('streamerListFilter', UI_TEXT.static.streamerListFilterPlaceholder);
|
||||||
setTitle('btnStreamerBulkRemove', UI_TEXT.static.streamerBulkRemoveTitle);
|
setTitle('btnStreamerBulkRemove', UI_TEXT.static.streamerBulkRemoveTitle);
|
||||||
|
setAriaLabel('btnStreamerBulkRemove', UI_TEXT.static.streamerBulkRemoveTitle);
|
||||||
|
setAriaLabel('btnAddStreamer', UI_TEXT.static.streamerAddAriaLabel);
|
||||||
|
setTitle('btnAddStreamer', UI_TEXT.static.streamerAddAriaLabel);
|
||||||
setText('metadataCacheMinutesLabel', UI_TEXT.static.metadataCacheMinutesLabel);
|
setText('metadataCacheMinutesLabel', UI_TEXT.static.metadataCacheMinutesLabel);
|
||||||
setText('filenameTemplatesTitle', UI_TEXT.static.filenameTemplatesTitle);
|
setText('filenameTemplatesTitle', UI_TEXT.static.filenameTemplatesTitle);
|
||||||
setText('vodTemplateLabel', UI_TEXT.static.vodTemplateLabel);
|
setText('vodTemplateLabel', UI_TEXT.static.vodTemplateLabel);
|
||||||
@ -282,6 +290,7 @@ function applyLanguageToStaticUI(): void {
|
|||||||
setPlaceholder('newStreamer', UI_TEXT.static.streamerPlaceholder);
|
setPlaceholder('newStreamer', UI_TEXT.static.streamerPlaceholder);
|
||||||
setPlaceholder('vodFilterInput', UI_TEXT.vods.filterPlaceholder);
|
setPlaceholder('vodFilterInput', UI_TEXT.vods.filterPlaceholder);
|
||||||
setTitle('vodFilterClearBtn', UI_TEXT.vods.filterClearTitle);
|
setTitle('vodFilterClearBtn', UI_TEXT.vods.filterClearTitle);
|
||||||
|
setAriaLabel('vodFilterClearBtn', UI_TEXT.vods.filterClearTitle);
|
||||||
setText('vodSortLabel', UI_TEXT.vods.sortLabel);
|
setText('vodSortLabel', UI_TEXT.vods.sortLabel);
|
||||||
if (typeof refreshVodSortSelectLabels === 'function') {
|
if (typeof refreshVodSortSelectLabels === 'function') {
|
||||||
refreshVodSortSelectLabels();
|
refreshVodSortSelectLabels();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user