SKILL-08 — Éléments Temporels¶
Média, animations, et contrôle du mouvement
Critères couverts¶
| Critère RAAM | Intitulé | Niveau | WCAG |
|---|---|---|---|
| 8.1 | Média audio/vidéo sans image : transcription | [A] | 1.2.1 |
| 8.2 | Média synchronisé : sous-titres pour sourds | [A] | 1.2.2 |
| 8.3 | Média synchronisé : audiodescription | [AA] | 1.2.5 |
| 8.4 | Média en direct : sous-titres en temps réel | [AA] | 1.2.4 |
| 8.5 | Contenus en mouvement automatiques : contrôles | [A] | 2.2.2 |
| 8.6 | Animations respectent Reduce Motion | [A] | 2.3.1, 2.3.3 |
Pourquoi c'est important¶
Réduire les animations (Reduce Motion)
Les troubles vestibulaires touchent environ 35% des adultes de plus de 40 ans. Une animation de parallaxe ou un zoom rapide peut provoquer des vertiges réels et des nausées. Pensez toujours à désactiver ces animations lorsque l'utilisateur a configuré "Reduce Motion" dans son OS.
Patterns¶
1. Reduce Motion (Animations adaptées) [A]¶
✅ Bon
@Environment(\.accessibilityReduceMotion) private var reduceMotion
// ...
RoundedRectangle(cornerRadius: 12)
.transition(reduceMotion
? .opacity // (1)!
: .move(edge: .bottom).combined(with: .opacity)
)
❌ Mauvais
Button("Ouvrir") {
withAnimation(.spring(response: 0.3, dampingFraction: 0.2)) { // (2)!
isExpanded.toggle()
}
}
- Fondu sécurisé pour les utilisateurs sensibles, au lieu du mouvement.
- Animation avec effet de ressort forcé (vertige potentiel).
✅ Bon
// Voir Helper rememberReduceMotion() dans patterns.kt
val reduceMotion = rememberReduceMotion()
val enterTransition = if (reduceMotion) fadeIn() else fadeIn() + slideInVertically() // (1)!
val exitTransition = if (reduceMotion) fadeOut() else fadeOut() + slideOutVertically()
AnimatedVisibility(
visible = isVisible,
enter = enterTransition,
exit = exitTransition
) {
Text("Contenu Animé")
}
- Utilise l'animation système ou détecte si l'échelle est à 0.
2. Transcription textuelle (Média audio seul) [A]¶
3. Contrôle des contenus automatiques (Carousels) [A]¶
✅ Bon
Button {
isAutoPlaying.toggle() // (1)!
} label: {
Label(
isAutoPlaying ? "Mettre en pause" : "Reprendre",
systemImage: isAutoPlaying ? "pause.fill" : "play.fill"
)
}
.accessibilityLabel(isAutoPlaying ? "Mettre en pause" : "Reprendre le défilement")
- Présence d'un bouton explicite pour stopper le carrousel. Label adaptatif.
✅ Bon
val playPauseDesc = if (isPlaying) "Mettre en pause le carrousel" else "Reprendre"
IconButton(
onClick = { isPlaying = !isPlaying },
modifier = Modifier.semantics { contentDescription = playPauseDesc }
) {
Icon(
imageVector = if (isPlaying) Icons.Filled.Pause else Icons.Filled.PlayArrow,
contentDescription = null
)
}
4. Vidéos et Sous-titres (AVPlayer/ExoPlayer) [A]¶
5. Messages temporaires (Snackbar, Toast) [A]¶
Un message qui disparaît seul est un contenu limité dans le temps (WCAG 2.2.1 [A]) ; les messages d'état doivent aussi être annoncés (WCAG 4.1.3 [AA]).
✅ Bon — Snackbar avec action et fermeture, sans disparition automatique
snackbarHostState.showSnackbar(
message = "Article supprimé",
actionLabel = "Annuler",
withDismissAction = true,
duration = SnackbarDuration.Indefinite
)
Pour un message d'état, annoncer avec AccessibilityNotification.Announcement("…").post() (iOS 17+) et laisser un bouton de fermeture pour tout message affiché en surimpression.
Checklist [A]¶
- Tout contenu audio seul a une transcription textuelle accessible.
- Toutes les vidéos avec parole ont des sous-titres (Closed Captions préférés).
- Les carousels auto-play ont un bouton Pause/Arrêt accessible.
- Le bouton Play/Pause a un
accessibilityLabeladaptatif. - L'application lit la préférence Reduce Motion du système.
- Les animations slide/spring/bounce sont remplacées par fade si Reduce Motion est actif.
- Aucun élément ne clignote plus de 3 fois par seconde (prévention crise épilepsie).
Checklist [AA]¶
- Les vidéos avec informations visuelles critiques ont une piste d'audiodescription sélectionnable.
- Les vidéos en direct avec parole ont des sous-titres en temps réel.
- Une option in-app permet de désactiver les autoplay indépendamment du système.