Courant Mis à jour : 2026-04

Patterns ARIA concrets pour WordPress FSE

Les 8 composants ARIA les plus courants dans les sites WordPress FSE : HTML minimal conforme, ce que le lecteur d'écran annonce, erreurs fréquentes en audit, et corrections spécifiques aux blocs natifs.

Table des matières

Les articles précédents ont posé les bases théoriques. Celui-ci est opérationnel : huit composants courants sur les sites WordPress Full Site Editing (FSE), avec pour chacun le HTML minimal conforme, les annonces exactes de NVDA, les erreurs fréquentes en audit, et les spécificités des blocs natifs Gutenberg.

Chaque pattern suit la même structure :

  1. HTML conforme : la structure minimale correcte
  2. Ce que NVDA annonce : transcription exacte sur Firefox
  3. Erreurs fréquentes : ce qu’on observe en audit
  4. Spécificité WordPress FSE : comportement du bloc natif et corrections

1. Menu de navigation avec sous-menus

Le composant le plus audité, le plus souvent incorrect.

HTML conforme

<nav aria-label="Navigation principale">
  <ul>
    <li>
      <a href="/" aria-current="page">Accueil</a>
    </li>
    <li>
      <a href="/mairie"
         aria-haspopup="true"
         aria-expanded="false"
         aria-controls="sous-menu-mairie"
         id="lien-mairie">
        Mairie
      </a>
      <ul id="sous-menu-mairie" role="list">
        <li><a href="/mairie/conseil">Conseil municipal</a></li>
        <li><a href="/mairie/horaires">Horaires d'accueil</a></li>
      </ul>
    </li>
  </ul>
</nav>

Ce que NVDA annonce

[Navigation au landmark navigation]
→ "Navigation principale, navigation, région de repère"

[Tab sur "Accueil"]
→ "Accueil, page actuelle, lien"

[Tab sur "Mairie"]
→ "Mairie, sous-menu, réduit, lien"

[Entrée sur "Mairie" — ouverture du sous-menu]
→ "Mairie, sous-menu, développé, lien"

[Tab sur "Conseil municipal"]
→ "Conseil municipal, lien"

Erreurs fréquentes en audit

role="menu" sur <ul> de navigation : impose un comportement clavier (flèches) que les utilisateurs n’attendent pas sur une navigation de site. Utiliser simplement <ul> sans rôle.

aria-current absent sur le lien actif : l’état actif est visible visuellement (CSS .current-menu-item) mais muet pour les AT.

Plusieurs <nav> sans aria-label distinct : dans la liste des landmarks, l’utilisateur entend “navigation, navigation, navigation” sans pouvoir distinguer principale, pied de page, fil d’ariane, etc.

Sous-menu ouvert sans mise à jour d’aria-expanded : le bouton dit “réduit” mais le menu est visible. Désynchronisation état ARIA / état réel.

Spécificité WordPress FSE

Le bloc Navigation (core/navigation) génère nativement aria-label sur le <nav> via l’option “Étiquette ARIA” dans l’inspecteur de bloc. À vérifier que cette option est renseignée, elle est vide par défaut.

aria-current="page" est généré automatiquement par WordPress sur le lien actif depuis la version 6.1. À vérifier dans les thèmes enfants qui surchargent wp_nav_menu().

Pour les sous-menus, les blocs Interactivity API (WordPress 6.5+) gèrent aria-expanded nativement. Les versions antérieures nécessitent un script personnalisé ou un plugin (Accessibility Checker, WP Accessibility).

Critères RGAA : 6.1 , 6.2 , 7.1 , 7.3 , 9.2 , 12.6 , 12.7 .

2. Bouton burger (menu mobile)

HTML conforme

<!-- Le bouton -->
<button
  type="button"
  aria-label="Ouvrir le menu"
  aria-expanded="false"
  aria-controls="menu-principal"
>
  <svg aria-hidden="true" focusable="false" viewBox="0 0 24 24">
    <path d="M3 12h18M3 6h18M3 18h18" stroke="currentColor" stroke-width="2"/>
  </svg>
</button>

<!-- Le menu contrôlé -->
<nav id="menu-principal" aria-label="Navigation principale" hidden>
  <ul>
    <li><a href="/">Accueil</a></li>
    <li><a href="/contact">Contact</a></li>
  </ul>
</nav>
const btn = document.querySelector('[aria-controls="menu-principal"]');
const menu = document.getElementById('menu-principal');

btn.addEventListener('click', () => {
  const estOuvert = btn.getAttribute('aria-expanded') === 'true';

  btn.setAttribute('aria-expanded', String(!estOuvert));
  btn.setAttribute('aria-label', estOuvert ? 'Ouvrir le menu' : 'Fermer le menu');

  if (estOuvert) {
    menu.setAttribute('hidden', '');
  } else {
    menu.removeAttribute('hidden');
    // Déplacer le focus sur le premier lien du menu
    menu.querySelector('a')?.focus();
  }
});

// Fermeture sur Échap
document.addEventListener('keydown', (e) => {
  if (e.key === 'Escape' && btn.getAttribute('aria-expanded') === 'true') {
    btn.setAttribute('aria-expanded', 'false');
    btn.setAttribute('aria-label', 'Ouvrir le menu');
    menu.setAttribute('hidden', '');
    btn.focus(); // retour au déclencheur
  }
});

Ce que NVDA annonce

[Tab sur le bouton burger, menu fermé]
→ "Ouvrir le menu, bouton réduit"

[Entrée — ouverture]
→ "Fermer le menu, bouton développé"
→ [focus déplacé] "Accueil, lien"

[Échap — fermeture]
→ [focus retourné au bouton] "Ouvrir le menu, bouton réduit"

Erreurs fréquentes en audit

aria-label statique : le bouton dit “Ouvrir le menu” même quand le menu est ouvert. Mettre à jour le label en même temps qu’aria-expanded.

Icône SVG sans aria-hidden : si le SVG a un <title>, NVDA peut annoncer à la fois le <title> et l’aria-label du bouton. Toujours aria-hidden="true" focusable="false" sur les SVG décoratifs.

Focus non déplacé à l’ouverture : l’utilisateur ouvre le menu et son focus reste sur le bouton. Il doit tabber manuellement jusqu’aux liens. Déplacer le focus sur le premier item du menu.

Focus non restauré à la fermeture : l’utilisateur ferme le menu (Échap ou clic) et son focus disparaît. Le retourner au bouton déclencheur.

Spécificité WordPress FSE

Le bloc Navigation inclut un bouton burger natif activé via l’option “Afficher le bouton de menu mobile” dans l’inspecteur. Il génère aria-expanded mais pas toujours aria-label explicite, vérifier le rendu HTML dans les DevTools. Le comportement focus (déplacement à l’ouverture, retour à la fermeture) dépend du thème FSE.

Critères RGAA : 7.1 , 7.3 .

3. Accordéon

HTML conforme

<div class="accordeon">

  <h3>
    <button
      type="button"
      aria-expanded="false"
      aria-controls="panneau-1"
      id="btn-1"
    >
      Quels sont les horaires d'ouverture ?
    </button>
  </h3>
  <div id="panneau-1" role="region" aria-labelledby="btn-1" hidden>
    <p>Lundi au vendredi, 9h-17h. Fermé le mercredi après-midi.</p>
  </div>

  <h3>
    <button
      type="button"
      aria-expanded="false"
      aria-controls="panneau-2"
      id="btn-2"
    >
      Comment prendre rendez-vous ?
    </button>
  </h3>
  <div id="panneau-2" role="region" aria-labelledby="btn-2" hidden>
    <p>Par téléphone au 02 98 XX XX XX ou en ligne.</p>
  </div>

</div>

Ce que NVDA annonce

[Tab sur le premier bouton, accordéon fermé]
→ "Quels sont les horaires d'ouverture ?, bouton réduit"

[Entrée — ouverture]
→ "Quels sont les horaires d'ouverture ?, bouton développé"

[Tab — entrée dans le panneau]
→ "Quels sont les horaires d'ouverture ?, région"
→ "Lundi au vendredi, 9h-17h. Fermé le mercredi après-midi."

Erreurs fréquentes en audit

<div> à la place de <button> : un <div class="accordeon-titre"> avec onclick n’est pas focusable, pas activable au clavier, non annoncé comme bouton. Utiliser <button> ou <a>.

aria-expanded sur le panneau plutôt que sur le déclencheur : le lecteur d’écran n’annonce l’état que sur l’élément où se trouve le focus.

Titre absent : le bouton sans <h2>, <h3>… fait partie du contenu mais n’apparaît pas dans la navigation par titres (touche H dans NVDA). Envelopper le bouton dans un titre de niveau approprié.

role="region" sans label : role="region" sans aria-labelledby ne génère pas de landmark. Inutile de l’ajouter si le panneau n’a pas de label.

Spécificité WordPress FSE

WordPress FSE intègre un bloc Accordéon natif depuis WordPress 6.8 (Interactivity API). Le markup généré est de bonne qualité :

  • <button type="button"> dans un <h3>, sémantique correcte, présent dans la navigation par titres
  • aria-expanded mis à jour dynamiquement via data-wp-bind--aria-expanded
  • Panneau masqué avec l’attribut inert : bloque à la fois le focus clavier et la lecture AT, plus fiable que hidden seul
  • aria-expanded + aria-labelledby sur le panneau, landmark nommé

Point à surveiller en audit : aria-labelledby sur le panneau référence l’id du bouton entier, pas seulement le span du titre. NVDA peut annoncer le nom complet du bouton comme label de la région (verbeux mais conforme). À tester avec NVDA + Firefox sur le site réel. Prérequis : le bloc natif nécessite l’Interactivity API (WordPress 6.5+) et JavaScript activé. Sans JS, aria-expanded reste à sa valeur initiale, vérifier le comportement en JS désactivé si la conformité totale est requise.

Pour les sites sur WordPress < 6.8, les plugins (Stackable, Kadence Blocks) restent nécessaires : vérifier systématiquement que le déclencheur est un <button> et qu’aria-expanded est mis à jour au clic.

Critères RGAA : 7.1 , 7.3 .

4. Interface à onglets

HTML conforme

<div class="onglets">

  <div role="tablist" aria-label="Sections du dossier">
    <button
      role="tab"
      id="tab-infos"
      aria-selected="true"
      aria-controls="panel-infos"
    >
      Informations
    </button>
    <button
      role="tab"
      id="tab-docs"
      aria-selected="false"
      aria-controls="panel-docs"
      tabindex="-1"
    >
      Documents
    </button>
    <button
      role="tab"
      id="tab-contact"
      aria-selected="false"
      aria-controls="panel-contact"
      tabindex="-1"
    >
      Contact
    </button>
  </div>

  <div role="tabpanel" id="panel-infos" aria-labelledby="tab-infos">
    <p>Informations générales…</p>
  </div>
  <div role="tabpanel" id="panel-docs" aria-labelledby="tab-docs" hidden>
    <p>Documents disponibles…</p>
  </div>
  <div role="tabpanel" id="panel-contact" aria-labelledby="tab-contact" hidden>
    <p>Coordonnées…</p>
  </div>

</div>

Navigation clavier obligatoire :

  • Tab : entre dans la liste d’onglets, se pose sur l’onglet sélectionné
  • : navigation entre onglets (pas Tab)
  • Entrée ou Espace : sélectionne l’onglet et affiche le panneau
  • Tab depuis un onglet : saute directement au panneau actif

Ce que NVDA annonce

[Tab — entrée dans le tablist]
→ "Sections du dossier, barre d'onglets"
→ "Informations, onglet sélectionné, 1 sur 3"

[→ flèche droite]
→ "Documents, onglet, 2 sur 3"

[Entrée — sélection]
→ "Documents, onglet sélectionné, 2 sur 3"

[Tab — saut au panneau]
→ "Documents, panneau d'onglet"
→ "Documents disponibles…"

Erreurs fréquentes en audit

Navigation aux onglets avec Tab au lieu des flèches : crée des pièges clavier. Les onglets non sélectionnés doivent avoir tabindex="-1".

aria-selected absent sur les onglets non sélectionnés : ne pas omettre aria-selected="false". Sans lui, NVDA n’annonce pas l’état des onglets non actifs.

Panneaux sans aria-labelledby : les panneaux ne sont pas identifiés. L’utilisateur arrive dans un panneau sans savoir à quel onglet il correspond.

Focus non déplacé au panneau après sélection : l’utilisateur active un onglet et son focus reste sur le bouton. Il doit tabber pour atteindre le contenu.

Spécificité WordPress FSE

Aucun bloc onglets natif dans Gutenberg. Les plugins (Stackable, Kadence Blocks) implémentent souvent les onglets avec <div> cliquables et navigation à Tab (non conforme). Vérifier systématiquement le comportement clavier et l’implémentation ARIA.

Critères RGAA : 7.1 , 7.3 .

5. Fenêtre modale (dialog)

HTML conforme

<!-- Déclencheur -->
<button type="button" aria-haspopup="dialog" id="btn-ouvrir-modal">
  Voir les conditions d'utilisation
</button>

<!-- Modale -->
<div
  role="dialog"
  aria-labelledby="titre-modal"
  aria-describedby="desc-modal"
  aria-modal="true"
  id="modal"
  hidden
>
  <h2 id="titre-modal">Conditions d'utilisation</h2>
  <p id="desc-modal">Veuillez lire et accepter les conditions avant de continuer.</p>

  <div class="contenu-modal">
    <p>Lorem ipsum…</p>
  </div>

  <div class="actions-modal">
    <button type="button" id="btn-accepter">Accepter</button>
    <button type="button" id="btn-fermer">Fermer</button>
  </div>
</div>

<!-- Fond semi-transparent -->
<div class="overlay" hidden aria-hidden="true"></div>

Gestion du focus obligatoire :

const modal      = document.getElementById('modal');
const btnOuvrir  = document.getElementById('btn-ouvrir-modal');
const btnFermer  = document.getElementById('btn-fermer');

// Éléments focusables dans la modale
const focusables = 'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])';

function ouvrirModal() {
  modal.removeAttribute('hidden');
  document.getElementById('overlay').removeAttribute('hidden');
  // Piéger le focus dans la modale
  modal.addEventListener('keydown', piegerFocus);
  // Focus sur le premier élément interactif
  modal.querySelector(focusables)?.focus();
}

function fermerModal() {
  modal.setAttribute('hidden', '');
  document.getElementById('overlay').setAttribute('hidden', '');
  modal.removeEventListener('keydown', piegerFocus);
  // Retour au déclencheur
  btnOuvrir.focus();
}

function piegerFocus(e) {
  if (e.key !== 'Tab') return;
  const elements = [...modal.querySelectorAll(focusables)];
  const premier  = elements[0];
  const dernier  = elements[elements.length - 1];

  if (e.shiftKey && document.activeElement === premier) {
    e.preventDefault();
    dernier.focus();
  } else if (!e.shiftKey && document.activeElement === dernier) {
    e.preventDefault();
    premier.focus();
  }
}

// Fermeture sur Échap
document.addEventListener('keydown', (e) => {
  if (e.key === 'Escape' && !modal.hasAttribute('hidden')) fermerModal();
});

btnOuvrir.addEventListener('click', ouvrirModal);
btnFermer.addEventListener('click', fermerModal);

Ce que NVDA annonce

[Ouverture de la modale]
→ "Conditions d'utilisation, boîte de dialogue"
→ "Veuillez lire et accepter les conditions avant de continuer."
→ [focus sur premier bouton] "Accepter, bouton"

[Tab dans la modale]
→ Navigation entre Accepter, Fermer — puis retour à Accepter

[Échap]
→ [focus retourné] "Voir les conditions d'utilisation, bouton boîte de dialogue"

Erreurs fréquentes en audit

Focus non piégé : l’utilisateur clavier peut sortir de la modale et interagir avec le contenu en arrière-plan. Erreur critique.

Focus non déplacé à l’ouverture : le focus reste sur le bouton déclencheur. L’utilisateur doit deviner qu’une modale s’est ouverte.

Focus non restauré à la fermeture : le focus se perd, souvent renvoyé au début de la page.

aria-modal="true" seul sans gestion du focus : aria-modal indique aux AT que le fond est inerte, mais ne piège pas réellement le focus. La gestion JavaScript reste obligatoire.

Fond semi-transparent focusable : l’overlay cliquable pour fermer doit avoir aria-hidden="true" et ne pas être dans l’ordre de tabulation.

Spécificité WordPress FSE

Le bloc natif <dialog> HTML5 (supporté par Chrome 37+, Firefox 98+, Safari 15.4+) simplifie l’implémentation. Les plugins de lightbox et de modale (FancyBox, Popup Maker) gèrent rarement le focus correctement (vérifier systématiquement).

Critères RGAA : 7.1 , 7.3 , 7.4 .

6. Carrousel et diaporama

Le composant le plus complexe à rendre accessible, et le plus fréquent sur les sites publics.

HTML conforme minimal

<section
  aria-label="Photos de la commune"
  aria-roledescription="carrousel"
>

  <!-- Contrôles -->
  <div class="controles-carrousel">
    <button type="button" aria-label="Diapositive précédente" id="btn-prev">
      <svg aria-hidden="true" focusable="false"><!-- flèche gauche --></svg>
    </button>
    <button type="button" aria-label="Pause du carrousel" id="btn-pause">
      <svg aria-hidden="true" focusable="false"><!-- pause --></svg>
    </button>
    <button type="button" aria-label="Diapositive suivante" id="btn-next">
      <svg aria-hidden="true" focusable="false"><!-- flèche droite --></svg>
    </button>
  </div>

  <!-- Piste de diapositives -->
  <div
    aria-live="polite"
    aria-atomic="false"
    class="piste-carrousel"
  >

    <div
      role="group"
      aria-roledescription="diapositive"
      aria-label="1 sur 5"
      aria-hidden="false"
    >
      <img src="photo-1.jpg" alt="Vue du port de Primelin au coucher du soleil">
    </div>

    <div
      role="group"
      aria-roledescription="diapositive"
      aria-label="2 sur 5"
      aria-hidden="true"
    >
      <img src="photo-2.jpg" alt="La chapelle Saint-Tugen en automne">
    </div>

  </div>

  <!-- Indicateurs de position -->
  <div role="group" aria-label="Choisir une diapositive">
    <button aria-label="Diapositive 1" aria-current="true"></button>
    <button aria-label="Diapositive 2"></button>
  </div>

</section>

Ce que NVDA annonce

[Entrée dans la section]
→ "Photos de la commune, carrousel"

[Tab sur le bouton précédent]
→ "Diapositive précédente, bouton"

[Changement automatique de diapositive]
→ "2 sur 5, diapositive" (annonce polite — attend fin lecture)

[Tab sur l'image]
→ "La chapelle Saint-Tugen en automne, image"

Erreurs fréquentes en audit

Pas de bouton pause : si le carrousel défile automatiquement, un contrôle pause/lecture accessible au clavier est obligatoire (critère 13.1 et 13.8).

Images sans alt : les diapositives sont des images porteuses d’information. alt="" est une NC sévère sur un carrousel de patrimoine.

aria-live="assertive" sur la piste : interrompt le lecteur d’écran à chaque changement. Utiliser "polite" pour les carrousels.

Défilement automatique sans prefers-reduced-motion : les utilisateurs sensibles aux animations peuvent désactiver les effets. Respecter cette préférence.

@media (prefers-reduced-motion: reduce) {
  .carrousel-auto { animation: none; }
}
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
  clearInterval(intervalAuto);
}

Spécificité WordPress FSE

Le bloc Galerie (core/gallery) ne génère pas de carrousel, il crée une grille d’images statiques, accessible si les alt sont renseignés. Les carrousels viennent de plugins (Easing Slider, Soliloquy, Smart Slider). Ces plugins génèrent rarement aria-roledescription, aria-live, ou des boutons pause accessibles. La recommandation en audit : préférer une galerie statique à un carrousel automatique.

Critères RGAA : 1.1 , 7.1 , 7.3 , 13.1 , 13.8 .

7. Formulaire avec validation

HTML conforme

<form novalidate>

  <p>Les champs marqués d'un <span aria-hidden="true">*</span>
  <span class="sr-only">astérisque</span> sont obligatoires.</p>

  <!-- Zone d'erreurs globales — live region -->
  <div role="alert" id="erreurs-globales" aria-label="Erreurs dans le formulaire"></div>

  <!-- Champ texte avec aide et erreur -->
  <div class="champ">
    <label for="nom">
      Nom <span aria-hidden="true">*</span>
    </label>
    <input
      type="text"
      id="nom"
      name="nom"
      autocomplete="family-name"
      aria-required="true"
      aria-describedby="aide-nom erreur-nom"
      aria-invalid="false"
    >
    <p id="aide-nom" class="aide">Votre nom de famille tel qu'il apparaît sur vos documents officiels.</p>
    <p id="erreur-nom" role="alert" class="erreur" hidden>
      Le nom est obligatoire.
    </p>
  </div>

  <!-- Champ email -->
  <div class="champ">
    <label for="email">
      Adresse email <span aria-hidden="true">*</span>
    </label>
    <input
      type="email"
      id="email"
      name="email"
      autocomplete="email"
      aria-required="true"
      aria-describedby="erreur-email"
      aria-invalid="false"
    >
    <p id="erreur-email" role="alert" class="erreur" hidden>
      Saisissez une adresse email valide (ex. : nom@exemple.fr).
    </p>
  </div>

  <button type="submit">Envoyer</button>

</form>

Validation et gestion des erreurs :

form.addEventListener('submit', (e) => {
  e.preventDefault();
  const erreurs = [];

  // Valider chaque champ
  const nom = document.getElementById('nom');
  if (!nom.value.trim()) {
    marquerErreur(nom, 'erreur-nom', 'Le nom est obligatoire.');
    erreurs.push('Nom : obligatoire');
  } else {
    effacerErreur(nom, 'erreur-nom');
  }

  if (erreurs.length > 0) {
    // Remplir la zone d'erreurs globales (live region)
    const zone = document.getElementById('erreurs-globales');
    zone.innerHTML = `<p>${erreurs.length} erreur${erreurs.length > 1 ? 's' : ''} à corriger :</p>
      <ul>${erreurs.map(e => `<li>${e}</li>`).join('')}</ul>`;
    // Focus sur le premier champ en erreur
    document.querySelector('[aria-invalid="true"]')?.focus();
  }
});

function marquerErreur(input, idErreur, message) {
  input.setAttribute('aria-invalid', 'true');
  const msgErreur = document.getElementById(idErreur);
  msgErreur.textContent = message;
  msgErreur.removeAttribute('hidden');
}

function effacerErreur(input, idErreur) {
  input.setAttribute('aria-invalid', 'false');
  const msgErreur = document.getElementById(idErreur);
  msgErreur.setAttribute('hidden', '');
  msgErreur.textContent = '';
}

Ce que NVDA annonce

[Focus sur le champ Nom, vide, avant soumission]
→ "Nom, zone de saisie, requis, Votre nom de famille tel qu'il apparaît…"

[Soumission avec champ vide]
→ [live region alert] "Le nom est obligatoire."
→ [focus déplacé] "Nom, zone de saisie, invalide, requis, Votre nom de famille…, Le nom est obligatoire."

[Après correction]
→ "Nom, zone de saisie, requis, Votre nom de famille…"

Erreurs fréquentes en audit

Message d’erreur non associé au champ : le message s’affiche visuellement sous le champ mais n’est pas référencé par aria-describedby. L’utilisateur AT corrige un champ sans entendre le message d’erreur.

aria-invalid non mis à jour : reste à "false" même après erreur. Le champ n’est pas annoncé comme invalide.

Focus non déplacé sur le premier champ en erreur après soumission : l’utilisateur ne sait pas quels champs corriger.

autocomplete absent : les champs nom, prénom, email, téléphone doivent avoir autocomplete. Critère 11.13 RGAA.

Spécificité WordPress FSE

Le plugin Contact Form 7 génère des messages d’erreur mais pas aria-invalid ni aria-describedby. Les messages sont dans des spans avec classes CSS, non liés programmatiquement aux champs. WPForms et Gravity Forms ont une meilleure implémentation ARIA mais à vérifier au cas par cas. Le bloc Formulaire natif Gutenberg (WordPress 6.4+) est encore limité (préférer CF7 ou WPForms avec vérification ARIA).

Critères RGAA : 11.1 , 11.2 , 11.9 , 11.10 , 11.11 .

8. Liens et boutons avec icônes

Le cas le plus fréquent et le plus simple, et pourtant source d’erreurs systématiques.

Les quatre cas à distinguer

Cas 1 : Icône décorative dans un lien/bouton avec texte visible

<!-- L'icône ne porte pas d'information — le texte suffit -->
<a href="/contact">
  <svg aria-hidden="true" focusable="false"><!-- enveloppe --></svg>
  Nous contacter
</a>
→ NVDA : "Nous contacter, lien"

Cas 2 : Icône seule dans un lien/bouton

<!-- L'icône est le seul contenu — aria-label obligatoire sur le lien -->
<a href="https://facebook.com/macommune"
   aria-label="Page Facebook de la commune (nouvelle fenêtre)"
   target="_blank"
   rel="noopener noreferrer">
  <svg aria-hidden="true" focusable="false"><!-- logo Facebook --></svg>
</a>
→ NVDA : "Page Facebook de la commune (nouvelle fenêtre), lien"

Cas 3 : SVG inline porteur d’information (pas dans un lien)

<!-- Le SVG lui-même est une image informative -->
<svg role="img" aria-label="Note : 4 étoiles sur 5" viewBox="0 0 100 20">
  <title>Note : 4 étoiles sur 5</title>
  <!-- étoiles -->
</svg>
→ NVDA : "Note : 4 étoiles sur 5, graphique"

Cas 4 : Texte sr-only dans un bouton icône

<!-- Alternative au aria-label : texte masqué dans le DOM -->
<button type="button">
  <svg aria-hidden="true" focusable="false"><!-- fermer --></svg>
  <span class="sr-only">Fermer la fenêtre</span>
</button>
→ NVDA : "Fermer la fenêtre, bouton"

Avantage du cas 4 vs cas 2 : le texte sr-only est traduisible par les outils de traduction automatique (Google Translate), alors qu’aria-label ne l’est pas toujours.

Erreurs fréquentes en audit

Liens réseaux sociaux avec aria-label="Facebook" : pas d’organisation mentionnée. Si plusieurs partenaires ont leurs réseaux sur la page, les liens sont indistinguables. Toujours préciser l’organisation : "Page Facebook de la Mairie de Nom_Ville".

target="_blank" sans mention : l’ouverture dans un nouvel onglet est un changement de contexte. Le mentionner dans l’aria-label : "(nouvelle fenêtre)" ou "(s'ouvre dans un nouvel onglet)".

SVG sans focusable="false" : certains navigateurs (Edge Legacy, IE) rendent les SVG focusables par défaut. Toujours ajouter focusable="false" sur les SVG avec aria-hidden.

alt sur <img> dans un lien décrivant l’image et non la destination : <a href="/contact"><img src="icone-email.png" alt="Icône email"></a> annonce “Icône email, lien”. L’alt devrait décrire la destination : alt="Nous contacter".

Spécificité WordPress FSE

Le bloc Icônes sociales (core/social-links) génère des <a> avec aria-label basé sur le nom du réseau ("Facebook", "Instagram"). Il ne mentionne pas l’organisation ni l’ouverture dans un nouvel onglet. Corriger via un filtre PHP ou dans les options du bloc si la version le permet.

Critères RGAA : 1.1 , 1.2 , 6.1 , 6.2 .

Récapitulatif : Les vérifications rapides en audit

ComposantVérifier en 30 secondes
Navigationaria-label sur <nav>, aria-current="page" sur lien actif
Bouton burgeraria-expanded mis à jour, focus déplacé à l’ouverture
Accordéon<button> dans un titre, aria-expanded sur le bouton
OngletsFlèches entre onglets, aria-selected sur tous les tabs
ModaleFocus piégé, déplacé à l’ouverture, restauré à la fermeture
CarrouselBouton pause, alt sur les images, pas d’aria-live="assertive"
Formulairearia-describedby pointe vers le message d’erreur, aria-invalid mis à jour
Liens icônesaria-label avec organisation + (nouvelle fenêtre) si target="_blank"

La lettre de l'Atelier A11Y

Ressources pédagogiques, critères RGAA commentés et retours de terrain : une lettre mensuelle pour progresser sur l'accessibilité numérique, sans jargon.

  • Nouveaux articles et ressources pédagogiques
  • Critères RGAA décortiqués avec des exemples concrets
  • Bonnes pratiques et retours d'expérience terrain
S'abonner à la newsletter (s'ouvre dans un nouvel onglet)

Gratuit. Désabonnement possible à tout moment.