DonnIA
Toutes les ressources
GuideFondamentaux IA14 min

Des formulaires qui marchent vraiment

Validation au bon moment, messages d'erreur utiles, état de chargement et accessibilité, avec un exemple complet.


Un formulaire, c'est l'endroit où le visiteur fait un effort. Il tape, il attend, il espère que ça a marché. Chaque friction ajoutée à ce moment se paie en abandons, et les frictions sont presque toujours les mêmes : un message d'erreur qui apparaît avant qu'on ait fini de taper, un « Une erreur est survenue » qui ne dit rien, un bouton sur lequel on clique trois fois parce que rien n'indique que la première a été prise en compte.

Aucun de ces défauts n'est difficile à corriger. Ils viennent d'un enchaînement mal pensé, pas d'une limite technique.

Ce guide reprend cet enchaînement dans l'ordre où le visiteur le vit : saisie, validation, erreur, envoi, confirmation. Avec un exemple complet à la fin.

Avant de commencer

  • Un formulaire existant, même minimal
  • De quoi tester au clavier, sans souris
  • Une idée de ce que tu fais des données une fois reçues

1Associer chaque champ à son libellé

C'est la base et c'est le défaut le plus fréquent. Un texte posé à côté d'un champ n'est pas un libellé : il faut l'association explicite, par l'attribut for de l'élément label qui reprend l'id du champ.

<label for="email">Adresse e-mail</label>
<input id="email" name="email" type="email" autocomplete="email" required />

Ce que ça débloque, au-delà de l'accessibilité : cliquer sur le libellé place le curseur dans le champ, ce qui agrandit nettement la cible sur mobile.

Le texte d'indication ne remplace pas le libellé

Un placeholder disparaît dès qu'on tape. Le visiteur qui revient vérifier sa saisie ne sait plus ce qu'on lui demandait, et l'outil de remplissage automatique n'a rien pour s'accrocher. Utilise-le pour un exemple de format, jamais comme unique libellé.

Pense aussi à autocomplete avec la bonne valeur (email, name, tel, postal-code, street-address). Le navigateur remplit alors le formulaire en un geste, ce qui est le meilleur gain de conversion disponible pour un coût nul.

2Valider au bon moment

Le mauvais moment, c'est à chaque frappe. Un champ e-mail qui affiche « adresse invalide » dès la première lettre est agressif et inutile, puisque l'adresse est forcément incomplète.

Le bon moment est double : à la sortie du champ, puis à la soumission.

Le CSS sait faire cette distinction seul. Le pseudo-sélecteur :user-invalid ne s'applique qu'après que le visiteur a interagi avec le champ ou tenté de soumettre, contrairement à :invalid qui s'applique dès le chargement.

input:user-invalid {
  border-color: #b3261e;
}

input:user-valid {
  border-color: #2e6b41;
}

C'est disponible dans les navigateurs courants (référencé comme largement disponible depuis novembre 2023). Sur un projet devant couvrir des navigateurs plus anciens, il faut gérer l'état côté script.

3Écrire des messages d'erreur utiles

Un message d'erreur utile dit trois choses : quel champ, quel est le problème, quoi faire. La plupart des messages produits par défaut n'en disent qu'une.

Message inutileMessage utile
« Champ invalide »« L'adresse e-mail doit contenir un @, par exemple nom@domaine.fr »
« Erreur »« Ce mot de passe fait 6 caractères, il en faut au moins 12 »
« Une erreur est survenue »« Nous n'avons pas pu envoyer le message. Réessaie dans un instant, ou écris directement à contact@exemple.fr »
« Format incorrect »« Le numéro doit contenir 10 chiffres, sans espace »

Techniquement, un message d'erreur doit être relié au champ, pas seulement posé en dessous. Deux attributs ARIA le permettent :

  • aria-invalid sur le champ. Ses valeurs sont true, false, grammar et spelling ; toute autre valeur est traitée comme true.
  • aria-describedby sur le champ, pointant vers l'id de l'élément qui contient le message.

aria-errormessage existe aussi

L'attribut aria-errormessage est prévu spécifiquement pour référencer un message d'erreur, en complément de aria-invalid. Son support par les lecteurs d'écran est plus inégal que celui de aria-describedby, qui reste le choix le plus sûr en production. Si tu veux la ceinture et les bretelles, tu peux poser les deux.

4Montrer que l'envoi est en cours

Sans retour visible, le visiteur clique à nouveau. Trois choses à faire pendant l'envoi :

  1. Désactiver le bouton, pour éviter le double envoi.
  2. Changer son texte, « Envoyer » devient « Envoi en cours ». Un indicateur qui tourne seul ne dit rien à qui ne le voit pas.
  3. Annoncer le résultat dans une zone que le lecteur d'écran surveille, avec aria-live="polite" pour une confirmation, ou role="alert" pour une erreur, qui interrompt.

Le point que presque tout le monde oublie : après un envoi réussi, déplacer le focus sur le message de confirmation. Sinon le focus reste sur un bouton désactivé, et la personne qui navigue au clavier n'a aucun moyen de savoir ce qui s'est passé.

5Un exemple complet

Un champ, avec tout ce qui précède en place.

<form novalidate>
  <div class="champ">
    <label for="email">Adresse e-mail</label>
    <input
      id="email"
      name="email"
      type="email"
      autocomplete="email"
      required
      aria-describedby="email-aide email-erreur"
    />
    <p id="email-aide" class="aide">Pour te repondre. Rien d'autre.</p>
    <p id="email-erreur" class="erreur" hidden></p>
  </div>

  <button type="submit">Envoyer</button>
  <p id="statut" aria-live="polite"></p>
</form>
const formulaire = document.querySelector("form");
const champ = document.querySelector("#email");
const erreur = document.querySelector("#email-erreur");
const statut = document.querySelector("#statut");
const bouton = formulaire.querySelector("button");

function valider() {
  if (champ.validity.valid) {
    champ.removeAttribute("aria-invalid");
    erreur.hidden = true;
    return true;
  }
  champ.setAttribute("aria-invalid", "true");
  erreur.textContent = champ.validity.valueMissing
    ? "Indique une adresse e-mail pour qu'on puisse te repondre."
    : "L'adresse doit contenir un @, par exemple nom@domaine.fr";
  erreur.hidden = false;
  return false;
}

champ.addEventListener("blur", valider);

formulaire.addEventListener("submit", async (evenement) => {
  evenement.preventDefault();
  if (!valider()) {
    champ.focus();
    return;
  }

  bouton.disabled = true;
  bouton.textContent = "Envoi en cours";
  statut.textContent = "";

  try {
    await envoyer(new FormData(formulaire));
    statut.textContent = "Message envoye. Reponse sous 48 heures.";
    statut.focus();
  } catch {
    statut.textContent =
      "Envoi impossible. Reessaie, ou ecris a contact@exemple.fr";
  } finally {
    bouton.disabled = false;
    bouton.textContent = "Envoyer";
  }
});

L'attribut novalidate désactive les bulles natives du navigateur pour te laisser afficher tes propres messages. L'API de validation reste disponible : champ.validity expose l'état sans que tu réécrives les règles.

La validation côté navigateur ne protège rien

Tout ce qui précède améliore l'expérience. Ce n'est pas une sécurité : les contraintes du navigateur se contournent en une ligne. Toute donnée reçue doit être revalidée côté serveur, sans exception.

Faire vérifier

Ouvre [URL]. Pour chaque champ du formulaire : la balise, le nom
accessible et d'ou il vient, la presence d'un attribut autocomplete, et
si un message d'erreur lui est relie par aria-describedby ou
aria-errormessage. Puis soumets le formulaire vide et dis-moi ou va le
focus et ce qui est annonce.

Quelques prompts pour aller plus loin

ObjectifPrompt à adapter
Audit de libellésListe les champs de [URL] sans label associe, ou dont le seul libelle est un placeholder.
Réécrire les erreursVoici mes messages d'erreur. Reecris chacun pour dire quel champ, quel probleme et quoi faire, en moins de 15 mots.
Parcours clavierRemplis et soumets ce formulaire uniquement au clavier. Dis-moi ou le focus se perd et a quelle etape.
Cas limitesListe les saisies qui devraient etre rejetees par ce formulaire et ne le sont pas, cote navigateur comme cote serveur.
Remplissage automatiquePropose la valeur autocomplete correcte pour chaque champ de ce formulaire.

Tu as d’autres ressources comme ça ?

Ce que tu viens de lire est entièrement gratuit. Une nouvelle ressource part chaque semaine dans la newsletter : guides, modèles à copier et liens triés, sans compte à créer.

Cette page est en accès libre, n’hésite pas à la partager.