wpcf7-not-valid-tip : c’est la classe CSS que Contact Form 7 attribue par défaut à chaque message d’erreur de champ, visible en rouge sous le champ concerné après une tentative de soumission invalide. Le problème ne se voit pas dans le CSS mais dans le HTML généré : ce message n’est associé à aucune région annoncée dynamiquement, et un lecteur d’écran ne signale strictement rien au moment où il apparaît.
Un utilisateur voyant comprend immédiatement qu’un champ pose problème grâce à la couleur et à la position du message. Un utilisateur non-voyant, lui, reste au même endroit du formulaire sans aucune indication qu’une erreur vient de survenir, sauf à retourner manuellement au champ pour vérifier.
Diagnostic : un DOM mis à jour sans annonce
En inspectant le comportement après soumission, Contact Form 7 insère bien dynamiquement les messages d’erreur dans le DOM via JavaScript, mais le conteneur englobant, généralement une <div class="wpcf7-response-output">, ne porte par défaut ni role="alert" ni aria-live. Résultat : la mise à jour du DOM est invisible pour la technologie d’assistance, qui n’a aucune raison de la lire à voix haute puisque rien ne signale qu’il s’agit d’une zone à surveiller.
C’est un défaut classique de formulaire AJAX : le contenu change visuellement, mais rien dans le balisage n’indique qu’un changement mérite d’être annoncé immédiatement plutôt qu’ignoré silencieusement, comme le sont la plupart des mises à jour du DOM en continu.

Le correctif : encadrer la sortie de réponse avec aria-live
Contact Form 7 propose un hook dédié, wpcf7_form_response_output, qui filtre le HTML de la zone de réponse avant son insertion. On peut s’en servir pour ajouter les attributs manquants sans toucher aux fichiers du plugin :
add_filter( 'wpcf7_form_response_output', function ( $output, $class, $content, $contact_form, $result ) {
$status = $result->status;
if ( in_array( $status, array( 'validation_failed', 'spam', 'aborted' ), true ) ) {
$output = str_replace(
'class="' . esc_attr( $class ) . '"',
'class="' . esc_attr( $class ) . '" role="alert" aria-live="assertive"',
$output
);
}
return $output;
}, 10, 5 );
Le choix de aria-live="assertive" plutôt que polite est volontaire : une erreur de validation bloquante justifie une interruption immédiate de la lecture en cours, contrairement à une simple mise à jour de confort qu’on préférerait annoncer sans couper l’utilisateur.
Traiter aussi les champs individuels
Le correctif ci-dessus couvre le résumé global d’erreurs, mais chaque message individuel sous un champ mérite également d’être associé au champ concerné via aria-describedby, pour qu’un lecteur d’écran annonce l’erreur au moment où l’utilisateur revient sur le champ, pas seulement lors du résumé global.
- Ajouter un identifiant unique à chaque
span.wpcf7-not-valid-tipgénéré - Référencer cet identifiant dans l’attribut
aria-describedbydu champ correspondant, via un script exécuté après l’événementwpcf7invaliddéclenché par le plugin - Vérifier que l’attribut
aria-invalid="true"est bien positionné sur le champ en erreur, ce que Contact Form 7 fait déjà correctement par défaut
Vérifier le correctif sans lecteur d’écran complet
Pour un contrôle rapide sans monter un environnement NVDA complet, l’onglet Accessibility des outils de développement du navigateur permet de suivre en direct l’arbre d’accessibilité et de repérer si le rôle alert apparaît bien au bon moment. Un test complet à la voix reste néanmoins nécessaire avant mise en production, l’onglet navigateur ne garantissant pas le comportement exact de chaque lecteur d’écran.
Prévention
Ce défaut concerne en réalité la plupart des plugins de formulaire qui gèrent leur validation entièrement en JavaScript côté client sans prévoir de région annoncée par défaut. Avant d’adopter une extension de formulaire pour un projet, un test de soumission invalide au clavier avec un lecteur d’écran actif permet de repérer ce genre de lacune avant qu’elle ne se généralise sur plusieurs formulaires du même site.