Pourquoi les formulaires méritent une attention particulière
Les formulaires sont les endroits où vos utilisateurs effectuent des paiements, s’inscrivent, effectuent des recherches et envoient des messages. C’est aussi là que la frustration s’accumule : un label manquant, une erreur confuse ou un champ de saisie qui ouvre le mauvais clavier peuvent vous faire perdre un utilisateur en quelques secondes.
La bonne nouvelle, c’est que HTML résout déjà la majeure partie de ces problèmes. Avec des labels appropriés, les bons types d’input et des contraintes natives, vous obtenez des formulaires accessibles, adaptés aux mobiles et validés avant même d’écrire une seule ligne de JavaScript.
Structure d’un formulaire
Un formulaire enveloppe ses contrôles et définit où et comment les soumettre.
<!-- signup.html -->
<form action="/signup" method="post">
<label for="email">Email address</label>
<input id="email" name="email" type="email" autocomplete="email" required />
<label for="password">Password</label>
<input id="password" name="password" type="password" minlength="8" required />
<button type="submit">Create account</button>
</form>
- action est l’URL qui reçoit les données.
- method est
getoupost. - name sur chaque contrôle est la clé dans les données soumises.
- type=“submit” déclenche la soumission ; un bouton simple à l’intérieur d’un formulaire est par défaut de type submit, utilisez donc
type="button"pour d’autres actions.
Labels
Chaque contrôle doit avoir un label. Un label est annoncé par les lecteurs d’écran, sert de zone de clic plus large et reste visible une fois le champ rempli.
<!-- label.html -->
<label for="phone">Phone number</label>
<input id="phone" name="phone" type="tel" autocomplete="tel" />
Vous pouvez également encapsuler le contrôle à l’intérieur du label, ce qui les associe implicitement :
<label>
Subscribe to the newsletter
<input type="checkbox" name="subscribe" />
</label>
Les placeholders ne sont pas des labels. Ils disparaissent dès que l’utilisateur commence à saisir du texte, présentent souvent un contraste insuffisant et ne sont pas annoncés de manière fiable. Utilisez un véritable label et, si nécessaire, une indication (hint).
Types d’entrées
Choisir le bon type permet d’afficher le clavier mobile approprié aux utilisateurs et d’activer la validation native.
| Type | Utilisation | Avantage |
|---|---|---|
| Adresses e-mail | Clavier e-mail, vérification du format | |
| tel | Numéros de téléphone | Pavé numérique |
| number | Valeurs numériques | Saisie numérique, min/max/step |
| url | Adresses web | Clavier URL, vérification du format |
| date | Dates | Sélecteur de date |
| password | Secrets | Saisie masquée |
| search | Champs de recherche | Clavier de recherche, bouton d’effacement |
| file | Téléchargements | Sélecteur de fichiers et filtrage par type |
Utilisez autocomplete avec des tokens standards comme email, given-name, postal-code et new-password pour que les navigateurs et les gestionnaires de mots de passe puissent aider l’utilisateur.
Groupement des contrôles
Groupez les contrôles liés afin que leur relation soit claire pour tout le monde.
<!-- address.html -->
<fieldset>
<legend>Shipping address</legend>
<label for="street">Street</label>
<input id="street" name="street" autocomplete="address-line1" />
<label for="city">City</label>
<input id="city" name="city" autocomplete="address-level2" />
</fieldset>
fieldset et legend permettent de grouper les boutons radio et les champs associés, ce que les lecteurs d’écran annoncent comme un ensemble. Pour les groupes de boutons radio, utilisez toujours un fieldset avec un legend.
Validation native
HTML propose la validation par contraintes via des attributs.
<!-- validate.html -->
<input type="email" name="email" required />
<input type="password" name="password" minlength="8" required />
<input type="number" name="age" min="18" max="120" step="1" />
<input type="text" name="handle" pattern="[a-z0-9_]{3,15}" />
<input type="url" name="website" />
Le navigateur bloque l’envoi du formulaire et affiche un message lorsqu’une contrainte n’est pas respectée. Vous pouvez styliser ces états avec CSS :
/* styles.css */
input:invalid { border-color: #dc2626; }
input:valid { border-color: #16a34a; }
input:focus-visible { outline: 2px solid #2563eb; }
Les messages natifs sont fonctionnels mais concis et non traduits. Pour les formulaires importants, proposez vos propres messages clairs en utilisant l’API Constraint Validation :
// validate.js
const email = document.querySelector("#email");
email.addEventListener("invalid", () => {
email.setCustomValidity("");
if (email.validity.valueMissing) {
email.setCustomValidity("Please enter your email address.");
} else if (email.validity.typeMismatch) {
email.setCustomValidity("That does not look like an email address.");
}
});
email.addEventListener("input", () => email.setCustomValidity(""));
N’oubliez pas d’effacer le message personnalisé lors de la saisie, sinon le champ restera considéré comme invalide.
Erreurs accessibles
La validation n’est utile que si les utilisateurs comprennent ce qui ne va pas.
- Affichez un message textuel clair à côté du champ, et pas seulement une bordure rouge.
- Associez-le au contrôle en utilisant
aria-describedby. - Marquez le champ avec
aria-invalid="true". - Déplacez le focus vers le premier champ invalide lors de la soumission.
- Résumez les erreurs en haut de page pour les formulaires longs.
<!-- error.html -->
<label for="email">Email address</label>
<input id="email" name="email" type="email" required
aria-invalid="true" aria-describedby="email-error" />
<p id="email-error" class="error">Please enter a valid email address.</p>
Ne vous appuyez jamais uniquement sur la couleur ; associez-la à du texte et à une icône.
Validez toujours côté serveur
La validation côté client est une fonctionnalité d’expérience utilisateur, et non un contrôle de sécurité. N’importe qui peut modifier la page, désactiver JavaScript ou envoyer une requête directement. Le serveur doit valider les types, les longueurs, les formats et les permissions, puis renvoyer des erreurs claires que le client pourra afficher. Consultez le guide sur la sécurité Web.
Bonnes pratiques
- Attribuez à chaque contrôle un label visible et associé.
- Utilisez le type d’input approprié et les tokens
autocomplete. - Groupez les contrôles liés à l’aide de
fieldsetetlegend. - Privilégiez les contraintes natives, puis améliorez-les avec la Constraint Validation API.
- Affichez des messages d’erreur accessibles et spécifiques à proximité du champ.
- Validez à nouveau côté serveur pour chaque requête.
- Gardez vos formulaires courts et ne demandez que les informations nécessaires.
Erreurs courantes
- Utiliser des placeholders au lieu de labels.
- Oublier les attributs
name, ce qui empêche l’envoi des champs. - Envoyer des données sensibles via GET.
- Se reposer uniquement sur la validation côté client pour la sécurité.
- Communiquer les erreurs uniquement par la couleur.
- Réinitialiser l’intégralité du formulaire lors d’une erreur de validation, entraînant la perte des saisies.
Et après ?
Les formulaires combinent le balisage, l’accessibilité et la validation. Renforcez votre balisage grâce au HTML sémantique, rendez-le inclusif avec le guide d’accessibilité, et gérez les soumissions avec JavaScript. Ensuite, auditez l’un des formulaires de votre application en vous appuyant sur la checklist ci-dessus.