Un client gérant un annuaire professionnel de plus de deux mille fiches nous a demandé de revoir la pagination de son moteur de recherche interne. Visuellement, la pagination fonctionnait très bien : numéros de page cliquables, page active mise en évidence par une couleur différente. Le problème se situait ailleurs, dans ce qui ne se voit pas : un utilisateur de lecteur d’écran qui changeait de page ne recevait aucune information sur ce changement, sinon en parcourant patiemment toute la nouvelle liste pour en déduire le contenu.
Le problème posé
La pagination était gérée en JavaScript, sans rechargement complet de la page, pour des raisons de rapidité perçue. Cette approche, courante et légitime pour la performance, pose un problème spécifique d’accessibilité : lorsqu’une portion de page change dynamiquement sans rechargement, aucun lecteur d’écran ne le détecte automatiquement, sauf si le développeur prend soin de le signaler explicitement via les mécanismes prévus à cet effet.
Snippet commenté : la structure de base

<nav aria-label="Pagination des résultats">
<ul>
<li><a href="?page=1" aria-current="page">1</a></li>
<li><a href="?page=2">2</a></li>
<li><a href="?page=3">3</a></li>
</ul>
</nav>
<div aria-live="polite" class="annonce-resultats" style="position:absolute; left:-9999px;"></div>
<ul id="liste-resultats">
<!-- fiches générées dynamiquement -->
</ul>
L’attribut aria-current="page" identifie sans ambiguïté, pour un lecteur d’écran, quel lien de pagination correspond à la page actuellement affichée, information que la seule différence de couleur visuelle ne transmet jamais à un utilisateur non-voyant. La zone masquée visuellement mais dotée de aria-live="polite" sert de canal d’annonce : tout texte inséré dedans est lu automatiquement par le lecteur d’écran, sans déplacer le focus de l’utilisateur, ce qui est essentiel pour ne pas l’interrompre brutalement dans sa lecture en cours.
Snippet commenté : le script de mise à jour
function chargerPage(numeroPage) {
fetch('/api/annuaire?page=' + numeroPage)
.then((response) => response.json())
.then((donnees) => {
afficherResultats(donnees.fiches);
document.querySelectorAll('nav[aria-label="Pagination des résultats"] a')
.forEach((lien) => lien.removeAttribute('aria-current'));
document.querySelector('a[href="?page=' + numeroPage + '"]')
.setAttribute('aria-current', 'page');
const annonce = document.querySelector('.annonce-resultats');
annonce.textContent = donnees.fiches.length + ' résultats affichés, page ' + numeroPage + ' sur ' + donnees.totalPages + '.';
});
}
Le texte inséré dans la zone d’annonce reprend volontairement le nombre exact de résultats affichés sur la page, plutôt qu’une formule vague du type « résultats mis à jour », qui n’apporterait aucune information exploitable à l’utilisateur.
Variante : pagination avec rechargement complet de page
Pour un site qui ne charge pas ses résultats en JavaScript mais recharge une page complète à chaque clic sur un numéro, la zone aria-live devient inutile : le rechargement de la page annonce déjà, via le titre de la page ou le premier titre rencontré, que le contenu a changé. Il reste néanmoins essentiel de conserver aria-current="page" sur le lien actif, souvent oublié même dans les paginations classiques sans JavaScript.
Variante : pagination avec bouton « Charger plus »
Sur un site utilisant un bouton « Charger plus » plutôt que des numéros de page, le principe reste identique, avec une nuance : l’annonce doit indiquer le nombre de résultats ajoutés, pas le nombre total affiché, pour rester cohérente avec l’action réalisée par l’utilisateur.
annonce.textContent = donnees.fiches.length + ' résultats supplémentaires chargés.';
Vérification avec un lecteur d’écran
- NVDA sous Firefox : annonce lue correctement à chaque changement de page
- VoiceOver sous Safari : annonce lue avec un léger délai, sans perte d’information
- Navigation clavier : le focus reste sur le lien de pagination cliqué, sans saut inattendu
Une annonce de résultats bien conçue ne doit jamais couper la parole à l’utilisateur : elle attend son tour, avec la politesse de l’attribut qui porte justement ce nom.
En résumé
Cette pagination, une fois complétée par aria-current et une zone d’annonce discrète, est passée d’un composant purement visuel à un composant réellement utilisable au clavier et au lecteur d’écran. Le coût d’implémentation est resté minime, quelques lignes de JavaScript et un attribut ARIA, pour un gain d’usage significatif sur un composant présent sur des centaines de pages du site.