improve accessibility; fixes #1177 (#1190)

This commit is contained in:
Cory LaViska
2025-07-17 11:56:41 -04:00
committed by GitHub
parent b98b9baba4
commit fe2c2ab7af
31 changed files with 98 additions and 7 deletions

View File

@@ -26,6 +26,7 @@ Components with the <wa-badge variant="warning">Experimental</wa-badge> badge sh
- Fixed a bug in `<wa-card>` that caused slotted media to have incorrectly rounded corners [issue:1107]
- Fixed a bug in `<wa-button-group>` that prevented pill buttons from rendering corners properly [issue:1165]
- Fixed a bug in `<wa-button-group>` that caused some vertical groups to appear horizontal [issue:1152]
- Improved accessibility of `<wa-animated-image>` so keyboard users can focus and toggle the animation [issue:1177]
## 3.0.0-beta.2

View File

@@ -42,7 +42,7 @@ img[aria-hidden='true'] {
}
}
:host([play]:not(:hover)) .control-box {
:where(:host([play]:not(:hover))) .control-box {
opacity: 0;
}
@@ -50,3 +50,16 @@ img[aria-hidden='true'] {
:host(:not([play])) slot[name='pause-icon'] {
display: none;
}
/* Show control box on keyboard focus */
.animated-image {
&:focus {
outline: none;
}
&:focus-visible .control-box {
opacity: 1;
outline: var(--wa-focus-ring);
outline-offset: var(--wa-focus-ring-offset);
}
}

View File

@@ -4,6 +4,7 @@ import { WaErrorEvent } from '../../events/error.js';
import { WaLoadEvent } from '../../events/load.js';
import { watch } from '../../internal/watch.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
import { LocalizeController } from '../../utilities/localize.js';
import '../icon/icon.js';
import styles from './animated-image.css';
@@ -30,6 +31,8 @@ import styles from './animated-image.css';
export default class WaAnimatedImage extends WebAwesomeElement {
static css = styles;
private readonly localize = new LocalizeController(this);
@query('.animated') animatedImage: HTMLImageElement;
@state() frozenFrame: string;
@@ -48,6 +51,13 @@ export default class WaAnimatedImage extends WebAwesomeElement {
this.play = !this.play;
}
private handleKeyDown(event: KeyboardEvent) {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
this.play = !this.play;
}
}
private handleLoad() {
const canvas = document.createElement('canvas');
const { width, height } = this.animatedImage;
@@ -82,15 +92,26 @@ export default class WaAnimatedImage extends WebAwesomeElement {
}
render() {
const verb = this.localize.term(this.play ? 'pauseAnimation' : 'playAnimation');
const label = `${verb} ${this.alt}`;
return html`
<div class="animated-image">
<div
class="animated-image"
tabindex="0"
role="button"
aria-pressed=${this.play ? 'true' : 'false'}
aria-label=${label}
@click=${this.handleClick}
@keydown=${this.handleKeyDown}
>
<img
class="animated"
src=${this.src}
alt=${this.alt}
crossorigin="anonymous"
aria-hidden=${this.play ? 'false' : 'true'}
@click=${this.handleClick}
role="presentation"
@load=${this.handleLoad}
@error=${this.handleError}
/>
@@ -102,10 +123,10 @@ export default class WaAnimatedImage extends WebAwesomeElement {
src=${this.frozenFrame}
alt=${this.alt}
aria-hidden=${this.play ? 'true' : 'false'}
@click=${this.handleClick}
role="presentation"
/>
<div part="control-box" class="control-box">
<div part="control-box" class="control-box" aria-hidden="true">
<slot name="play-icon">
<wa-icon
name="play"

View File

@@ -24,6 +24,8 @@ const translation: Translation = {
if (num > 2 && num < 11) return `تم تحديد ${num} خيارات`;
return `تم تحديد ${num} خيار`;
},
pauseAnimation: 'إيقاف الرسوم المتحركة مؤقتًا',
playAnimation: 'تشغيل الرسوم المتحركة',
previousSlide: 'الشريحة السابقة',
progress: 'مقدار التقدم',
remove: 'حذف',

View File

@@ -22,6 +22,8 @@ const translation: Translation = {
if (num === 1) return 'Je vybrána jedna možnost';
return `Počet vybraných možností: ${num}`;
},
pauseAnimation: 'Pozastavit animaci',
playAnimation: 'Přehrát animaci',
previousSlide: 'Předchozí slide',
progress: 'Průběh',
remove: 'Odstranit',

View File

@@ -22,6 +22,8 @@ const translation: Translation = {
if (num === 1) return '1 valgt';
return `${num} valgt`;
},
pauseAnimation: 'Pause animation',
playAnimation: 'Afspil animation',
previousSlide: 'Forrige dias',
progress: 'Status',
remove: 'Fjern',

View File

@@ -22,6 +22,8 @@ const translation: Translation = {
if (num === 1) return '1 Option ausgewählt';
return `${num} Optionen ausgewählt`;
},
pauseAnimation: 'Animation pausieren',
playAnimation: 'Animation abspielen',
previousSlide: 'Vorherige Folie',
progress: 'Fortschritt',
remove: 'Entfernen',

View File

@@ -22,6 +22,8 @@ const translation: Translation = {
if (num === 1) return '1 option selected';
return `${num} options selected`;
},
pauseAnimation: 'Pause animation',
playAnimation: 'Play animation',
previousSlide: 'Previous slide',
progress: 'Progress',
remove: 'Remove',

View File

@@ -22,6 +22,8 @@ const translation: Translation = {
if (num === 1) return '1 opción seleccionada';
return `${num} opción seleccionada`;
},
pauseAnimation: 'Pausar animación',
playAnimation: 'Reproducir animación',
previousSlide: 'Diapositiva anterior',
progress: 'Progreso',
remove: 'Eliminar',

View File

@@ -22,6 +22,8 @@ const translation: Translation = {
if (num === 1) return '1 گزینه انتخاب شده است';
return `${num} گزینه انتخاب شده است`;
},
pauseAnimation: 'مکث انیمیشن',
playAnimation: 'پخش انیمیشن',
previousSlide: 'اسلاید قبلی',
progress: 'پیشرفت',
remove: 'حذف',

View File

@@ -22,6 +22,8 @@ const translation: Translation = {
if (num === 1) return 'Yksi vaihtoehto valittu';
return `${num} vaihtoehtoa valittu`;
},
pauseAnimation: 'Keskeytä animaatio',
playAnimation: 'Toista animaatio',
previousSlide: 'Edellinen dia',
progress: 'Edistyminen',
remove: 'Poista',

View File

@@ -22,6 +22,8 @@ const translation: Translation = {
if (num === 1) return '1 option sélectionnée';
return `${num} options sélectionnées`;
},
pauseAnimation: "Suspendre l'animation",
playAnimation: "Lire l'animation",
previousSlide: 'Diapositive précédente',
progress: 'Progrès',
remove: 'Retirer',

View File

@@ -16,13 +16,15 @@ const translation: Translation = {
goToSlide: (slide, count) => `עבור לשקופית ${slide} של ${count}`,
hidePassword: 'הסתר סיסמא',
loading: 'טוען',
nextSlide: 'Next slide',
nextSlide: 'השקף הבא',
numOptionsSelected: num => {
if (num === 0) return 'לא נבחרו אפשרויות';
if (num === 1) return 'נבחרה אפשרות אחת';
return `נבחרו ${num} אפשרויות`;
},
previousSlide: 'Previous slide',
pauseAnimation: 'השהה אנימציה',
playAnimation: 'נגן אנימציה',
previousSlide: 'שקופית קודמת',
progress: 'התקדמות',
remove: 'לְהַסִיר',
resize: 'שנה גודל',

View File

@@ -22,6 +22,8 @@ const translation: Translation = {
if (num === 1) return '1 opcija je odabrana';
return `${num} odabranih opcija`;
},
pauseAnimation: 'Pauziraj animaciju',
playAnimation: 'Reproduciraj animaciju',
previousSlide: 'Prethodni slajd',
progress: 'Napredak',
remove: 'Makni',

View File

@@ -22,6 +22,8 @@ const translation: Translation = {
if (num === 1) return '1 lehetőség kiválasztva';
return `${num} lehetőség kiválasztva`;
},
pauseAnimation: 'Animáció szüneteltetése',
playAnimation: 'Animáció lejátszása',
previousSlide: 'Előző dia',
progress: 'Folyamat',
remove: 'Eltávolítás',

View File

@@ -22,6 +22,8 @@ const translation: Translation = {
if (num === 1) return '1 opsi yang dipilih';
return `${num} opsi yang dipilih`;
},
pauseAnimation: 'Jeda animasi',
playAnimation: 'Putar animasi',
previousSlide: 'Slide sebelumnya',
progress: 'Kemajuan',
remove: 'Hapus',

View File

@@ -22,6 +22,8 @@ const translation: Translation = {
if (num === 1) return '1 opzione selezionata';
return `${num} opzioni selezionate`;
},
pauseAnimation: 'Metti in pausa animazione',
playAnimation: 'Riproduci animazione',
previousSlide: 'Diapositiva precedente',
progress: 'Avanzamento',
remove: 'Rimuovi',

View File

@@ -21,6 +21,8 @@ const translation: Translation = {
if (num === 0) return '項目が選択されていません';
return `${num} 個の項目が選択されました`;
},
pauseAnimation: 'アニメーションを一時停止',
playAnimation: 'アニメーションを再生',
previousSlide: '前のスライド',
progress: '進行',
remove: '削除',

View File

@@ -22,6 +22,8 @@ const translation: Translation = {
if (num === 1) return 'Ett alternativ valgt';
return `${num} alternativer valgt`;
},
pauseAnimation: 'Sett animasjon på pause',
playAnimation: 'Spill av animasjon',
previousSlide: 'Forrige visning',
progress: 'Fremdrift',
remove: 'Fjern',

View File

@@ -22,6 +22,8 @@ const translation: Translation = {
if (num === 1) return '1 optie geselecteerd';
return `${num} opties geselecteerd`;
},
pauseAnimation: 'Animatie pauzeren',
playAnimation: 'Animatie afspelen',
previousSlide: 'Vorige dia',
progress: 'Voortgang',
remove: 'Verwijderen',

View File

@@ -22,6 +22,8 @@ const translation: Translation = {
if (num === 1) return 'Eitt alternativ valt';
return `${num} alternativ valt`;
},
pauseAnimation: 'Set animasjon på pause',
playAnimation: 'Spel av animasjon',
previousSlide: 'Førre visning',
progress: 'Framdrift',
remove: 'Fjern',

View File

@@ -22,6 +22,8 @@ const translation: Translation = {
if (num === 1) return 'Wybrano 1 opcję';
return `Wybrano ${num} opcje`;
},
pauseAnimation: 'Wstrzymaj animację',
playAnimation: 'Odtwórz animację',
previousSlide: 'Poprzedni slajd',
progress: 'Postęp',
remove: 'Usunąć',

View File

@@ -22,6 +22,8 @@ const translation: Translation = {
if (num === 1) return '1 opção selecionada';
return `${num} opções selecionadas`;
},
pauseAnimation: 'Pausar animação',
playAnimation: 'Reproduzir animação',
previousSlide: 'Slide anterior',
progress: 'Progresso',
remove: 'Remover',

View File

@@ -22,6 +22,8 @@ const translation: Translation = {
if (num === 1) return 'Выбран 1 вариант';
return `выбрано ${num} варианта`;
},
pauseAnimation: 'Приостановить анимацию',
playAnimation: 'Воспроизвести анимацию',
previousSlide: 'Предыдущий слайд',
progress: 'Прогресс',
remove: 'Удалить',

View File

@@ -24,6 +24,8 @@ const translation: Translation = {
if (num === 3 || num === 4) return `${num} možnosti izbrane`;
return `${num} možnosti izbranih`;
},
pauseAnimation: 'Zaustavi animacijo',
playAnimation: 'Predvajaj animacijo',
previousSlide: 'Prejšnji diapozitiv',
progress: 'Napredek',
remove: 'Odstrani',

View File

@@ -22,6 +22,8 @@ const translation: Translation = {
if (num === 1) return '1 alternativ valt';
return `${num} alternativ valda`;
},
pauseAnimation: 'Pausa animation',
playAnimation: 'Spela upp animation',
previousSlide: 'Föregående bild',
progress: 'Framsteg',
remove: 'Ta bort',

View File

@@ -22,6 +22,8 @@ const translation: Translation = {
if (num === 1) return '1 seçenek seçildi';
return `${num} seçenek seçildi`;
},
pauseAnimation: 'Animasyonu duraklat',
playAnimation: 'Animasyonu oynat',
previousSlide: 'Bir onceki slayt',
progress: 'İlerleme',
remove: 'Kaldır',

View File

@@ -24,6 +24,8 @@ const translation: Translation = {
if (n === 2 || n === 3 || n === 4) return `вибрано ${num} варіанти`;
return `вибрано ${num} варіантів`;
},
pauseAnimation: 'Призупинити анімацію',
playAnimation: 'Відтворити анімацію',
previousSlide: 'Попередній слайд',
progress: 'Поступ',
remove: 'Видалити',

View File

@@ -22,6 +22,8 @@ const translation: Translation = {
if (num === 1) return '已选择 1 个项目';
return `${num} 选择项目`;
},
pauseAnimation: '暂停动画',
playAnimation: '播放动画',
previousSlide: '上一张幻灯片',
progress: '进度',
remove: '删除',

View File

@@ -22,6 +22,8 @@ const translation: Translation = {
if (num === 1) return '已選擇 1 個項目';
return `${num} 選擇項目`;
},
pauseAnimation: '暫停動畫',
playAnimation: '播放動畫',
previousSlide: '上一張幻燈片',
progress: '進度',
remove: '移除',

View File

@@ -33,6 +33,8 @@ export interface Translation extends DefaultTranslation {
loading: string;
nextSlide: string;
numOptionsSelected: (num: number) => string;
pauseAnimation: string;
playAnimation: string;
previousSlide: string;
progress: string;
remove: string;