Aller au contenu principal

Accessibilité

Une animation est une image qui change dans le temps, et une image est précisément ce qu'un lecteur d'écran ne peut pas restituer. Le lecteur résout ce problème comme le moteur résout tout le reste : la timeline compilée est déjà une liste ordonnée d'événements, elle est donc rendue deux fois — une fois en pixels, une fois en phrases.

Les deux rendus viennent de la même fonction pure, dans l'esprit d'evaluate : ils ne peuvent pas diverger comme divergerait une légende écrite à la main.

Ce qu'un lecteur d'écran reçoit

Trois choses, dont aucune ne vous demande d'écrire quoi que ce soit :

  • Une région nommée. Le lecteur s'annonce au lieu d'être une boîte anonyme dans laquelle on tombe.
  • Une description textuelle — un résumé, puis une phrase par étape. Chaque phrase est un bouton qui déplace le lecteur jusqu'à cette étape : l'animation devient quelque chose que l'on explore à son rythme, pas un enregistrement que l'on subit. Un collègue voyant devant le même écran voit la scène suivre.
  • Une région live qui annonce chaque étape au moment où la tête de lecture y entre.

La scène elle-même porte aria-hidden. C'est délibéré : ses étiquettes sont positionnées en absolu, donc lues dans l'ordre du DOM elles forment un sac de chaînes sans lien (« NavigateurServeur WebGET /users ») où l'animation — ce qui bouge, vers où, et pourquoi — n'apparaît nulle part. C'est la description qui porte cette information.

Chargement…

La transcription ci-dessus est affichée (transcript="visible") pour que vous puissiez lire ce qu'un lecteur d'écran entend. Par défaut elle est présente mais masquée visuellement — le lecteur a exactement l'apparence qu'il a toujours eue, avec un bouton pour révéler la description.

// Le défaut : disponible pour les technologies d'assistance, invisible à l'écran.
<DataFlowPlayer spec={spec} />

// Affichée, en panneau de transcription sous la scène.
<DataFlowPlayer spec={spec} transcript="visible" />

Écrire la description vous-même

Les phrases générées disent ce qui se passe. Elles ne peuvent pas dire pourquoi, parce que la spec ne le sait pas : elle sait qu'un paquet est allé d'un nœud à un autre, pas qu'il y est allé parce que le cache était vide.

Deux champs optionnels comblent cet écart. Aucun n'est obligatoire, et une animation qui s'en passe reste entièrement décrite.

ChampCe qu'il remplace
descriptionsur la specLa première phrase du résumé
descriptionsur une action racineLa phrase générée pour cette étape
{
description: "Comment un chargement de page atteint la base et en revient",
timeline: [
{
type: 'move', object: 'req', from: 'browser', to: 'api',
description: "L'utilisateur ouvre la page, le navigateur demande la liste",
},
// Sans `description` : « SELECT * FROM users va de Serveur Web vers Base de données. »
{ type: 'move', object: 'sql', from: 'api', to: 'db' },
],
}
Chargement…

Une action comment n'en a jamais besoin : son text est déjà votre propre narration de l'étape, et il est repris tel quel.

Comment un élément est nommé

Une étape doit dire CE QUI s'est déplacé, donc chaque élément est nommé à partir de la première chose réellement prononçable :

  1. Le texte qu'il porte — l'en-tête d'un paquet HTTP, le SQL d'une requête, la valeur d'une pastille, le text d'un nœud.
  2. À défaut, ce qu'il est — « une réponse SQL de 12 lignes », « un paquet HTTP ». L'identifiant d'un paquet est votre poignée sur lui, pas un nom qu'un auditeur peut utiliser : « rows va de Base de données vers API » ne nomme rien.

Un libellé fait uniquement d'espaces, de ponctuation ou de symboles ne compte pas comme du texte : un lecteur d'écran les épelle caractère par caractère (« espace », « flèche vers la droite »), donc header: '→' retombe sur la nature du paquet. Un libellé qui mêle symboles et mots — GET / 🔒 — est conservé tel quel.

Localiser la description

Les phrases sont construites à partir de gabarits dans labels, aux côtés des chaînes de la barre de contrôle — describeMove vaut '{object} travels from {from} to {to}'. L'ordre des mots vous appartient : une langue qui place le verbe en fin de phrase peut l'y placer.

<DataFlowPlayer
spec={spec}
labels={{
describeMove: '{object} va de {from} vers {to}',
describeLoading: '{object} travaille',
}}
/>

Tout gabarit que vous n'indiquez pas garde son défaut anglais : vous pouvez traduire ceux qui comptent pour vous et vous arrêter là.

Le mouvement

Le lecteur ne démarre jamais seul : autoPlay est désactivé par défaut, et la barre de contrôle permet de mettre en pause. Si vous activez autoPlay, respectez prefers-reduced-motion — une animation en boucle avec controls={false} est un mouvement que le visiteur ne peut pas arrêter, ce que WCAG 2.2.2 interdit.

const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;

<DataFlowPlayer spec={spec} autoPlay={!reduced} loop={!reduced} controls />;

La désactiver

transcript="none" supprime la description. Comme la scène est un décor aria-hidden, il ne reste alors aucun moyen de lire l'animation pour un utilisateur de lecteur d'écran — ne l'utilisez que si la même information est déjà présente ailleurs dans la page.

Obtenir la description sans lecteur

describeAnimation est exporté par le cœur. Il prend une spec et sa timeline compilée, et retourne le résumé et les étapes — pour une page statique listant ce que fait chaque animation, un export, ou une plage braille.

import {
compile,
describeAnimation,
DEFAULT_PLAYER_LABELS,
} from '@dataflow-animator/core';

const { timeline } = compile(spec);
const { summary, steps } = describeAnimation(
spec,
timeline,
DEFAULT_PLAYER_LABELS
);