get_field_object() renvoie un tableau complet avec label, name et value, mais rien ne garantit qu’un développeur restitue ce label sous la forme d’un vrai <label> lié au champ correspondant. C’est précisément le piège rencontré sur un gabarit de formulaire front-end construit à partir d’un groupe de champs Advanced Custom Fields, où chaque champ ressemblait visuellement à un formulaire classique sans en avoir la structure.
Ce texte ne traite pas de la validation des données saisies côté serveur, déjà couverte ailleurs, mais spécifiquement du lien manquant entre libellé affiché et champ de saisie généré dynamiquement à partir d’ACF.
Le symptôme : un formulaire qui a l’air normal
Le gabarit affichait, pour chaque champ du groupe ACF, un paragraphe contenant le libellé en gras suivi d’un champ de saisie sur la ligne suivante :
<?php foreach ( $fields as $field ) : ?>
<p><strong><?php echo esc_html( $field['label'] ); ?></strong></p>
<input type="text" name="<?php echo esc_attr( $field['name'] ); ?>">
<?php endforeach; ?>
Visuellement, rien ne choque : le libellé apparaît juste au-dessus du champ, dans le bon ordre de lecture. Mais aucun <label> n’entoure ni ne référence ce texte, et le champ ne possède ni id, ni aria-label, ni aria-labelledby. Un lecteur d’écran annonce simplement « champ de texte », sans aucun contexte.
Pourquoi ACF ne force rien automatiquement
Advanced Custom Fields, côté formulaire d’administration, génère bien ses propres labels correctement associés dans l’écran d’édition. Mais dès qu’un développeur reconstruit un formulaire front-end à partir de get_field_objects() ou d’une boucle sur un groupe de champs, cette association devient entièrement sa responsabilité : ACF fournit les données brutes, pas le markup accessible.
Le cas des champs répétables
Le problème s’aggrave sur un champ répétable (repeater), où le même nom de champ logique se répète plusieurs fois dans le formulaire. Sans identifiant unique généré à chaque itération, plusieurs champs peuvent se retrouver avec le même id, ce qui casse également l’association pour les navigateurs les plus tolérants.

Le correctif : générer un identifiant unique à partir de la clé du champ
La solution consiste à construire un id unique pour chaque champ, généralement à partir de son name ACF combiné à un index de boucle, et à englober réellement le libellé dans un <label> pointant vers cet identifiant :
<?php foreach ( $fields as $index => $field ) :
$champ_id = 'champ-' . $field['name'] . '-' . $index;
?>
<label for="<?php echo esc_attr( $champ_id ); ?>">
<?php echo esc_html( $field['label'] ); ?>
</label>
<input type="text" id="<?php echo esc_attr( $champ_id ); ?>"
name="<?php echo esc_attr( $field['name'] ); ?>">
<?php endforeach; ?>
Pour un champ répétable, ajoutez systématiquement l’index de la ligne courante dans l’identifiant généré, afin qu’aucune collision ne se produise même avec dix ou vingt lignes ajoutées dynamiquement par l’utilisateur.
Le cas des champs sans label visuel
Certains designs se passent volontairement de libellé visible, en s’appuyant sur un texte d’exemple (placeholder) directement dans le champ. Cette pratique reste un anti-pattern : un placeholder disparaît dès la première frappe et n’est pas systématiquement restitué par les technologies d’assistance de la même manière qu’un label. Utilisez alors aria-label comme solution de repli, jamais comme substitut par défaut au vrai label.
- Toujours un
<label>associé parfor/id, y compris sur les champs répétables - Un identifiant unique par itération de boucle, jamais réutilisé
aria-labelseulement en dernier recours, sur un champ vraiment sans libellé visuel possible
Vérifier le résultat avec l’inspecteur d’accessibilité du navigateur
Une fois le correctif appliqué, ouvrez l’onglet Accessibilité des DevTools et cliquez sur chaque champ généré : le panneau doit afficher un nom accessible identique au texte du libellé visible. Si le nom accessible reste vide malgré la présence d’un <label>, vérifiez que l’attribut for correspond exactement, caractère pour caractère, à l’id du champ.
Un formulaire ACF qui « a l’air » correctement labellisé à l’écran ne l’est réellement que si le lien for/id existe dans le markup généré, jamais par simple proximité visuelle.
En résumé
ACF fournit les données du champ, jamais leur association accessible : générez systématiquement un identifiant unique par champ, y compris dans une boucle répétable, et reliez-le explicitement à son libellé avec un vrai <label> plutôt qu’un simple paragraphe en gras placé au bon endroit visuellement.